From 70172bf8e468196f9cb55594ca91d02a657d2a1d Mon Sep 17 00:00:00 2001 From: Andreas <38214111+2flea@users.noreply.github.com> Date: Sun, 3 Nov 2019 16:20:20 +0100 Subject: [PATCH 01/56] Add temporary PS remaining time in profile name --- .../plugins/general/overview/OverviewFragment.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index 5c2a7cac84..80c7c588bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -58,6 +58,7 @@ import info.nightscout.androidaps.data.QuickWizardEntry; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.ExtendedBolus; +import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; @@ -76,6 +77,7 @@ import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; @@ -1227,6 +1229,14 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (profile.getPercentage() != 100 || profile.getTimeshift() != 0) { activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonWarning)); activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextWarning)); + + TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); + ProfileSwitch profileSwitch = activeTreatments.getProfileSwitchFromHistory(System.currentTimeMillis()); + if (profileSwitch != null) { + if (profileSwitch.profileJson != null && profileSwitch.durationInMinutes != 0) { + activeProfileView.setText(ProfileFunctions.getInstance().getProfileName() + DateUtil.untilString(profileSwitch.originalEnd())); + } + } } else { activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault)); activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextDefault)); From 7371ae3315858602b48eed96f7427109dabb26e5 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Mon, 4 Nov 2019 11:55:34 +0000 Subject: [PATCH 02/56] - added DetailedBolusInfoStorage add to deliverBolus --- .../pump/medtronic/MedtronicPumpPlugin.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index ae6d754493..f2ee74275c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -48,6 +48,7 @@ import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperAc import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; @@ -372,7 +373,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter refreshAnyStatusThatNeedsToBeRefreshed(); } - RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); + RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); } @@ -386,7 +387,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter RileyLinkServiceState rileyLinkServiceState = MedtronicUtil.getServiceState(); - if (rileyLinkServiceState==null) { + if (rileyLinkServiceState == null) { LOG.error("RileyLink unreachable. RileyLinkServiceState is null."); return false; } @@ -744,13 +745,13 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter ClockDTO clock = MedtronicUtil.getPumpTime(); - if (clock==null) { // retry + if (clock == null) { // retry medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock); clock = MedtronicUtil.getPumpTime(); } - if (clock==null) + if (clock == null) return; int timeDiff = Math.abs(clock.timeDifference); @@ -867,6 +868,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter } TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); + DetailedBolusInfoStorage.INSTANCE.add(detailedBolusInfo); // we subtract insulin, exact amount will be visible with next remainingInsulin update. getMDTPumpStatus().reservoirRemainingUnits -= detailedBolusInfo.insulin; @@ -1065,10 +1067,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile, boolean enforceNew) { - if (percent==0) { + if (percent == 0) { return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); } else { - double absoluteValue = profile.getBasal() * (percent /100.0d); + double absoluteValue = profile.getBasal() * (percent / 100.0d); getMDTPumpStatus(); absoluteValue = pumpStatusLocal.pumpType.determineCorrectBasalSize(absoluteValue); LOG.warn("setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (%d). This will start setTempBasalAbsolute, with calculated value (%.3f). Result might not be 100% correct.", percent, absoluteValue); From 220bd20f1fcf051d643b98bdd2b9be8bbf6ccbd5 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Mon, 4 Nov 2019 12:21:29 +0000 Subject: [PATCH 03/56] medtronic-2.5.1.1 - DetailedBolusInfo.add is used when Bolus delivered - fixed some logging problems with Gson for core objects - when bolus is given, date is changed to when it was actually delivered (in case contacting pump takes longer than exprected) --- app/build.gradle | 2 +- .../pump/medtronic/MedtronicPumpPlugin.java | 7 ++++++- .../pump/medtronic/data/MedtronicHistoryData.java | 15 ++++++++++++++- .../pump/medtronic/util/MedtronicUtil.java | 8 ++++---- .../plugins/treatments/TreatmentsPlugin.java | 4 ++-- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 94aaa78834..4ad49d22ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,7 @@ android { targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.5.1" + version "medtronic-2.5.1.1" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index f2ee74275c..f5b882eaf2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -867,6 +867,11 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter }).start(); } + long now = System.currentTimeMillis(); + + detailedBolusInfo.date = now; + detailedBolusInfo.deliverAt = now; // not sure about that one + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); DetailedBolusInfoStorage.INSTANCE.add(detailedBolusInfo); @@ -879,7 +884,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter // calculate time for bolus and set driver to busy for that time int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d); - long time = System.currentTimeMillis() + (bolusTime * 1000); + long time = now + (bolusTime * 1000); this.busyTimestamps.add(time); setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 1eed440cdc..13fed8ba8b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -77,6 +77,7 @@ public class MedtronicHistoryData { private boolean isInit = false; private Gson gson; + private Gson gsonCore; private DatabaseHelper databaseHelper = MainApp.getDbHelper(); private ClockDTO pumpTime; @@ -94,10 +95,15 @@ public class MedtronicHistoryData { public MedtronicHistoryData() { this.allHistory = new ArrayList<>(); this.gson = MedtronicUtil.gsonInstance; + this.gsonCore = MedtronicUtil.getGsonInstanceCore(); if (this.gson == null) { this.gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); } + + if (this.gsonCore == null) { + this.gsonCore = new GsonBuilder().create(); + } } @@ -597,7 +603,7 @@ public class MedtronicHistoryData { if (doubleBolusDebug) LOG.debug("DoubleBolusDebug: List (before filter): {}, FromDb={}", gson.toJson(entryList), - gson.toJson(entriesFromHistory)); + gsonCore.toJson(entriesFromHistory)); filterOutAlreadyAddedEntries(entryList, entriesFromHistory); @@ -862,6 +868,7 @@ public class MedtronicHistoryData { return; List removeTreatmentsFromHistory = new ArrayList<>(); + List removeTreatmentsFromPH = new ArrayList<>(); for (DbObjectBase treatment : treatmentsFromHistory) { @@ -879,11 +886,17 @@ public class MedtronicHistoryData { if (selectedBolus != null) { entryList.remove(selectedBolus); + removeTreatmentsFromPH.add(selectedBolus); removeTreatmentsFromHistory.add(treatment); } } } + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: filterOutAlreadyAddedEntries: PumpHistory={}, Treatments={}", + gson.toJson(removeTreatmentsFromPH), + gsonCore.toJson(removeTreatmentsFromHistory)); + treatmentsFromHistory.removeAll(removeTreatmentsFromHistory); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java index 96bf0a5d44..2c7ee69c2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java @@ -61,8 +61,7 @@ public class MedtronicUtil extends RileyLinkUtil { private static int doneBit = 1 << 7; private static ClockDTO pumpTime; public static Gson gsonInstance = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - public static Gson gsonInstancePretty = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() - .setPrettyPrinting().create(); + public static Gson gsonInstanceCore = new GsonBuilder().create(); private static BatteryType batteryType = BatteryType.None; @@ -70,8 +69,9 @@ public class MedtronicUtil extends RileyLinkUtil { return gsonInstance; } - public static Gson getGsonInstancePretty() { - return gsonInstancePretty; + + public static Gson getGsonInstanceCore() { + return gsonInstanceCore; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index 28181da190..004f44858d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -349,7 +349,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface long time = System.currentTimeMillis(); synchronized (treatments) { if (MedtronicHistoryData.doubleBolusDebug) - log.debug("DoubleBolusDebug: AllTreatmentsInDb: {}", MedtronicUtil.getGsonInstance().toJson(treatments)); + log.debug("DoubleBolusDebug: AllTreatmentsInDb: {}", MedtronicUtil.getGsonInstanceCore().toJson(treatments)); for (Treatment t : treatments) { if (t.date <= time && t.date >= fromTimestamp) @@ -357,7 +357,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } if (MedtronicHistoryData.doubleBolusDebug) - log.debug("DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={}", fromTimestamp, MedtronicUtil.getGsonInstance().toJson(in5minback)); + log.debug("DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={}", fromTimestamp, MedtronicUtil.getGsonInstanceCore().toJson(in5minback)); return in5minback; } From 3797e7671a5d56d843a7cc392d541084603afc06 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Mon, 4 Nov 2019 12:25:21 +0000 Subject: [PATCH 04/56] - one missing core log --- .../plugins/pump/medtronic/data/MedtronicHistoryData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 13fed8ba8b..781f24b0ba 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -615,7 +615,7 @@ public class MedtronicHistoryData { if (doubleBolusDebug) LOG.debug("DoubleBolusDebug: List (after filter): {}, FromDb={}", gson.toJson(entryList), - gson.toJson(entriesFromHistory)); + gsonCore.toJson(entriesFromHistory)); if (isCollectionEmpty(entriesFromHistory)) { for (PumpHistoryEntry treatment : entryList) { From 2044fe6860fd2918ee53f8556f71a332a875b3e6 Mon Sep 17 00:00:00 2001 From: andreas <2flea@gmx.de> Date: Tue, 5 Nov 2019 15:43:11 +0100 Subject: [PATCH 05/56] Instead of computing name in overview fragment add function ProfileFunctions.getInstance().getProfileNameWithDuration() to be called --- .../configBuilder/ProfileFunctions.java | 26 ++++++++++++------- .../general/overview/OverviewFragment.java | 12 +-------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java index 025d8dac1e..91c20b095a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import io.reactivex.disposables.CompositeDisposable; @@ -75,35 +76,42 @@ public class ProfileFunctions { } public String getProfileName() { - return getProfileName(System.currentTimeMillis()); + return getProfileName(System.currentTimeMillis(), true, false); } public String getProfileName(boolean customized) { - return getProfileName(System.currentTimeMillis(), customized); + return getProfileName(System.currentTimeMillis(), customized, false); } - public String getProfileName(long time) { - return getProfileName(time, true); + public String getProfileNameWithDuration() { + return getProfileName(System.currentTimeMillis(), true, true); } - public String getProfileName(long time, boolean customized) { + public String getProfileName(long time, boolean customized, boolean showRemainingTime) { + String profileName = MainApp.gs(R.string.noprofileselected); + TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); ProfileInterface activeProfile = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); ProfileSwitch profileSwitch = activeTreatments.getProfileSwitchFromHistory(time); if (profileSwitch != null) { if (profileSwitch.profileJson != null) { - return customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName; + profileName = customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName; } else { ProfileStore profileStore = activeProfile.getProfile(); if (profileStore != null) { Profile profile = profileStore.getSpecificProfile(profileSwitch.profileName); if (profile != null) - return profileSwitch.profileName; + profileName = profileSwitch.profileName; } } + + if (showRemainingTime && profileSwitch.durationInMinutes != 0) { + profileName += DateUtil.untilString(profileSwitch.originalEnd()); + } + return profileName; } - return MainApp.gs(R.string.noprofileselected); + return profileName; } public boolean isProfileValid(String from) { @@ -176,7 +184,7 @@ public class ProfileFunctions { profileSwitch = new ProfileSwitch(); profileSwitch.date = System.currentTimeMillis(); profileSwitch.source = Source.USER; - profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false); + profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false, false); profileSwitch.profileJson = getInstance().getProfile().getData().toString(); profileSwitch.profilePlugin = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getClass().getName(); profileSwitch.durationInMinutes = duration; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index 80c7c588bf..046290500d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -58,7 +58,6 @@ import info.nightscout.androidaps.data.QuickWizardEntry; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; @@ -77,7 +76,6 @@ import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; @@ -1225,18 +1223,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, extendedBolusView.setVisibility(View.VISIBLE); } - activeProfileView.setText(ProfileFunctions.getInstance().getProfileName()); + activeProfileView.setText(ProfileFunctions.getInstance().getProfileNameWithDuration()); if (profile.getPercentage() != 100 || profile.getTimeshift() != 0) { activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonWarning)); activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextWarning)); - - TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); - ProfileSwitch profileSwitch = activeTreatments.getProfileSwitchFromHistory(System.currentTimeMillis()); - if (profileSwitch != null) { - if (profileSwitch.profileJson != null && profileSwitch.durationInMinutes != 0) { - activeProfileView.setText(ProfileFunctions.getInstance().getProfileName() + DateUtil.untilString(profileSwitch.originalEnd())); - } - } } else { activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault)); activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextDefault)); From 943d3c788ac159a9386975c324329325dfbbf370 Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Wed, 6 Nov 2019 12:21:31 +0100 Subject: [PATCH 06/56] Create CONTRIBUTING.md First draft of contribution guidelines --- CONTRIBUTING.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..f22d9cf3a1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +This document speciffy hints and good practices for source code contributions. + +AndroidAPS is community effort and all contributions are welcome! If you wish help us improving AndroidAPS - please read and try to adhere to +this guidelines, to make the development and process of change aproval as smooth as possible :) + +General rules +============= + +* There are plenty of ways you can help, some of them are listed on wiki: + https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/How-can-I-help.html +* If you wish to help with documentation or translating: + https://androidaps.readthedocs.io/en/latest/EN/translations.html + +Development guidelines +====================== + +Commiting Changes / Pull Requests +--------------------------------- + +1. Make fork of repository on github +2. Create separate branch for each feature, branch from most recent dev +3. Commit all changes to your fork +4. When ready, rebase on top of dev and make pull request to main repo + +Naming Conventions for Pull Requests / Branches +----------------------------------------------- + +TODO + +Translations +------------ + +* If possible, always use Android translation mechanism (with strings.xml and @strings/id) instead of hardcoded texts +* Provide only English strings - all other languages will be crowd translated via Crowdn https://translations.androidaps.org/ + +Hints +----- + +* Start small, it is easier to review smaller changes that affect fewer parts of code +* Take a look into Issues list (https://github.com/MilosKozak/AndroidAPS/issues) - maybe there is somthing you can fix or implement +* For new features, make sure there is Issue to track progress and have on-topic discussion +* Reach out to community, discuss idea on Gitter (https://gitter.im/MilosKozak/AndroidAPS) +* Speak with other developers to minimise merge conflicts. Find out who worked, working or plan to work on speciffic issue or part of app From d23faf65bff66fb5775a5d030d3d7258f1185340 Mon Sep 17 00:00:00 2001 From: Philoul Date: Wed, 6 Nov 2019 20:31:05 +0100 Subject: [PATCH 07/56] Add strings for shortday (shorthour allready exists) for translation of statuslights and remaining time TT in main screen --- .../java/info/nightscout/androidaps/db/CareportalEvent.java | 4 ++-- .../main/java/info/nightscout/androidaps/utils/DateUtil.java | 2 +- app/src/main/res/values/strings.xml | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index 40fadff684..1c33e8f1be 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -100,8 +100,8 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { String hours = " " + MainApp.gs(R.string.hours) + " "; if (useShortText) { - days = "d"; - hours = "h"; + days = MainApp.gs(R.string.shortday); + hours = MainApp.gs(R.string.shorthour); } return diff.get(TimeUnit.DAYS) + days + diff.get(TimeUnit.HOURS) + hours; diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index d592347d1b..8b3996a3dc 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -185,7 +185,7 @@ public class DateUtil { long remainingTimeMinutes = timeInMillis / (1000 * 60); long remainingTimeHours = remainingTimeMinutes / 60; remainingTimeMinutes = remainingTimeMinutes % 60; - return "(" + ((remainingTimeHours > 0) ? (remainingTimeHours + "h ") : "") + remainingTimeMinutes + "')"; + return "(" + ((remainingTimeHours > 0) ? (remainingTimeHours + MainApp.gs(R.string.shorthour) + " ") : "") + remainingTimeMinutes + "')"; } public static String sinceString(long timestamp) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70e05db6c4..3942e9c030 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -778,6 +778,7 @@ g m h + d ]]> kJ En From 86d4b755eadc72e17139d5f9c3a2b4ca8b050154 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Thu, 7 Nov 2019 10:39:17 +0100 Subject: [PATCH 08/56] 1) Added SMS commands: - SMS DISABLE/STOP to disable SMS Service; - TARGET MEAL/ACTIVITY/HYPO to switch to the standard temp targets; - BOLUS 0.60 MEAL, to do a bolus and set the stadard meal TT (ie 90 mg/dL for 45'). 2) Modified the SMS Timeout in a range from 3' to 60'. This preference is useful for parents that will manage many boluses in a single meal (because often, eaten foods are unpredictable with babies, so parents need to perform many closed boluses). Due to safety reasons (prevent multiple boluses for stolen phones, cloned SIM, ...): - Added a further SMS command to disable SMS Services (SMS DISABLE/STOP); - To modify the default timeout of 15', 2 Phone Numbers are mandatory In this way, if unwanted/suspicious boluses are notified, the other parent can disable Remote Management (till to re-enable it directly with the AAPS in the master smartphone). --- .../activities/PreferencesActivity.java | 35 ++++ .../SmsCommunicatorPlugin.java | 180 +++++++++++++++++- app/src/main/res/values/strings.xml | 11 ++ app/src/main/res/xml/pref_smscommunicator.xml | 13 +- 4 files changed, 234 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index 367429dbbf..bf69624c40 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -13,6 +13,7 @@ import android.preference.PreferenceScreen; import android.text.TextUtils; import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.bus.RxBus; @@ -51,6 +52,8 @@ import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; +import com.andreabaccega.widget.ValidatingEditTextPreference; + public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { MyPreferenceFragment myPreferenceFragment; @@ -218,6 +221,38 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre TidepoolUploader.INSTANCE.testLogin(getActivity()); return false; }); + + final ValidatingEditTextPreference distance = (ValidatingEditTextPreference)findPreference(getString(R.string.key_smscommunicator_remotebolusmindistance)); + final EditTextPreference allowedNumbers = (EditTextPreference)findPreference(getString(R.string.key_smscommunicator_allowednumbers)); + if (distance != null && allowedNumbers != null) { + if (!SmsCommunicatorPlugin.areMoreNumbers(allowedNumbers.getText())) { + distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + getString(R.string.smscommunicator_remotebolusmindistance_caveat)); + distance.setEnabled(false); + } else { + distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)); + distance.setEnabled(true); + } + + allowedNumbers.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (!SmsCommunicatorPlugin.areMoreNumbers(((String)newValue))) { + distance.setText(String.valueOf(Constants.remoteBolusMinDistance/(60 * 1000L))); + distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + getString(R.string.smscommunicator_remotebolusmindistance_caveat)); + distance.setEnabled(false); + } else { + distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)); + distance.setEnabled(true); + } + return true; + } + }); + } + } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java index 264d7c3e52..f34a66ca4b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java @@ -23,6 +23,7 @@ import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.interfaces.Constraint; @@ -136,6 +137,8 @@ public class SmsCommunicatorPlugin extends PluginBase { case "EXTENDED": case "CAL": case "PROFILE": + case "TARGET": + case "SMS": return true; } if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(number)) @@ -242,7 +245,7 @@ public class SmsCommunicatorPlugin extends PluginBase { sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)); else if (splitted.length == 2 && ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended()) sendSMS(new Sms(receivedSms.phoneNumber, R.string.pumpsuspended)); - else if (splitted.length == 2) + else if (splitted.length == 2 || splitted.length == 3) processBOLUS(splitted, receivedSms); else sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); @@ -255,6 +258,22 @@ public class SmsCommunicatorPlugin extends PluginBase { else sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); break; + case "TARGET": + if (!remoteCommandsAllowed) + sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); + else if (splitted.length == 2) + processTARGET(splitted, receivedSms); + else + sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); + break; + case "SMS": + if (!remoteCommandsAllowed) + sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); + else if (splitted.length == 2) + processSMS(splitted, receivedSms); + else + sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); + break; default: // expect passCode here if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(receivedSms.phoneNumber)) { messageToConfirm.action(splitted[0]); @@ -680,10 +699,19 @@ public class SmsCommunicatorPlugin extends PluginBase { private void processBOLUS(String[] splitted, Sms receivedSms) { Double bolus = SafeParse.stringToDouble(splitted[1]); + final boolean isMeal = splitted.length > 2 && splitted[2].equalsIgnoreCase("MEAL"); bolus = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolus)).value(); - if (bolus > 0d) { + + if (splitted.length == 3 && !isMeal) { + sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); + } else if (bolus > 0d) { String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode); + String reply = ""; + if (isMeal) { + reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode); + } else { + reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode); + } receivedSms.processed = true; messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(bolus) { @Override @@ -701,10 +729,40 @@ public class SmsCommunicatorPlugin extends PluginBase { public void run() { PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (resultSuccess) { - String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered); + String reply = ""; + if (isMeal) { + reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered); + } else { + reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered); + } if (pump != null) reply += "\n" + pump.shortStatus(true); lastRemoteBolusTime = DateUtil.now(); + if (isMeal) { + Profile currentProfile = ProfileFunctions.getInstance().getProfile(); + int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); + eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration; + double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl); + eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl; + + TempTarget tempTarget = new TempTarget() + .date(System.currentTimeMillis()) + .duration(eatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits())) + .high(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits())); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + String tt = ""; + if (currentProfile.getUnits().equals(Constants.MMOL)) { + tt = DecimalFormatter.to1Decimal(eatingSoonTT); + } else + tt = DecimalFormatter.to0Decimal(eatingSoonTT); + + reply += String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration); + + + } sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); } else { String reply = MainApp.gs(R.string.smscommunicator_bolusfailed); @@ -722,6 +780,105 @@ public class SmsCommunicatorPlugin extends PluginBase { sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); } + private void processTARGET(String[] splitted, Sms receivedSms) { + boolean isMeal = splitted[1].equalsIgnoreCase("MEAL"); + boolean isActivity = splitted[1].equalsIgnoreCase("ACTIVITY"); + boolean isHypo = splitted[1].equalsIgnoreCase("HYPO"); + + if (isMeal || isActivity || isHypo) { + String passCode = generatePasscode(); + String reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(), passCode); + receivedSms.processed = true; + messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { + @Override + public void run() { + try { + Profile currentProfile = ProfileFunctions.getInstance().getProfile(); + + int keyDuration = 0; + Integer defaultTargetDuration = 0; + int keyTarget = 0; + double defaultTargetMMOL = 0d; + double defaultTargetMGDL = 0d; + + if (isMeal) { + keyDuration = R.string.key_eatingsoon_duration; + defaultTargetDuration = Constants.defaultEatingSoonTTDuration; + keyTarget = R.string.key_eatingsoon_target; + defaultTargetMMOL = Constants.defaultEatingSoonTTmmol; + defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl; + } else if (isActivity) { + keyDuration = R.string.key_activity_duration; + defaultTargetDuration = Constants.defaultActivityTTDuration; + keyTarget = R.string.key_activity_target; + defaultTargetMMOL = Constants.defaultActivityTTmmol; + defaultTargetMGDL = Constants.defaultActivityTTmgdl; + + } else if (isHypo) { + keyDuration = R.string.key_hypo_duration; + defaultTargetDuration = Constants.defaultHypoTTDuration; + keyTarget = R.string.key_hypo_target; + defaultTargetMMOL = Constants.defaultHypoTTmmol; + defaultTargetMGDL = Constants.defaultHypoTTmgdl; + } + + int ttDuration = SP.getInt(keyDuration, defaultTargetDuration); + ttDuration = ttDuration > 0 ? ttDuration : defaultTargetDuration; + double tt = SP.getDouble(keyTarget, currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL); + tt = tt > 0 ? tt : currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL; + + TempTarget tempTarget = new TempTarget() + .date(System.currentTimeMillis()) + .duration(ttDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(tt, currentProfile.getUnits())) + .high(Profile.toMgdl(tt, currentProfile.getUnits())); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + String ttString = ""; + if (currentProfile.getUnits().equals(Constants.MMOL)) + ttString = DecimalFormatter.to1Decimal(tt); + else + ttString = DecimalFormatter.to0Decimal(tt); + + String reply = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration); + sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); + } catch (Exception e) { + sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); + return; + } + } + }); + } else + sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); + } + + private void processSMS(String[] splitted, Sms receivedSms) { + boolean isStop = splitted[1].equalsIgnoreCase("STOP") + || splitted[1].equalsIgnoreCase("DISABLE"); + + if (isStop) { + String passCode = generatePasscode(); + String reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode); + receivedSms.processed = true; + messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { + @Override + public void run() { + try { + SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); + String reply = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)); + sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); + } catch (Exception e) { + e.printStackTrace(); + sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); + return; + } + } + }); + } else + sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); + } + private void processCAL(String[] splitted, Sms receivedSms) { Double cal = SafeParse.stringToDouble(splitted[1]); if (cal > 0d) { @@ -809,4 +966,19 @@ public class SmsCommunicatorPlugin extends PluginBase { s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""); return s; } + + public static boolean areMoreNumbers(String allowednumbers) { + int countNumbers = 0; + String[] substrings = allowednumbers.split(";"); + + for (String number : substrings) { + String cleaned = number.replaceAll("\\s+", ""); + if (cleaned.length()<4) continue; + if (cleaned.substring(0,1).compareTo("+")!=0) continue; + cleaned = cleaned.replace("+",""); + if (!cleaned.matches("[0-9]+")) continue; + countNumbers++; + } + return countNumbers > 1; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c43ba1193..9f37cc9a85 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -292,11 +292,22 @@ Allowed phone numbers +XXXXXXXXXX;+YYYYYYYYYY To deliver bolus %1$.2fU reply with code %2$s + To deliver meal bolus %1$.2fU reply with code %2$s + To set the Temp Target %1$s reply with code %2$s + To disable the SMS Remote Service reply with code %1$s.\n\nKeep in mind that you\'ll able to reactivate it directly from the AAPS master smartphone only. + SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone. To send calibration %1$.2f reply with code %2$s Bolus failed + smscommunicator_remotebolusmindistance + Minimum number of minutes that must elapse between one remote bolus and the next + How many minutes must elapse, at least, between one bolus and the next + For your safety, to edit this preference you need to add at least 2 phone numbers. Bolus %1$.2fU delivered successfully Going to deliver %1$.2fU Bolus %1$.2fU delivered successfully + Meal Bolus %1$.2fU delivered successfully + Target %1$s for %2$d minutes + Target %1$s for %2$d minutes set succesfully Delivering %1$.2fU Allow remote commands via SMS Finger diff --git a/app/src/main/res/xml/pref_smscommunicator.xml b/app/src/main/res/xml/pref_smscommunicator.xml index 202e7348c5..e76b1b15b2 100644 --- a/app/src/main/res/xml/pref_smscommunicator.xml +++ b/app/src/main/res/xml/pref_smscommunicator.xml @@ -1,5 +1,6 @@ - + @@ -8,6 +9,16 @@ android:key="@string/key_smscommunicator_allowednumbers" android:summary="@string/smscommunicator_allowednumbers_summary" android:title="@string/smscommunicator_allowednumbers" /> + Date: Sun, 10 Nov 2019 13:37:04 +0100 Subject: [PATCH 09/56] gradle update --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 31658346b6..9618c68851 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.google.gms:google-services:4.3.2' classpath 'io.fabric.tools:gradle:1.31.0' From 2319536adc9cd34a884cf50deff0172aada402d5 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Mon, 11 Nov 2019 09:57:35 +0100 Subject: [PATCH 10/56] Cumulative corrections for Milos msgs in 2019, 7 Nov about try/catch and missed 'if null' contition --- .../SmsCommunicatorPlugin.java | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java index f34a66ca4b..f40d60523c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java @@ -792,9 +792,8 @@ public class SmsCommunicatorPlugin extends PluginBase { messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { @Override public void run() { - try { - Profile currentProfile = ProfileFunctions.getInstance().getProfile(); - + Profile currentProfile = ProfileFunctions.getInstance().getProfile(); + if (currentProfile != null) { int keyDuration = 0; Integer defaultTargetDuration = 0; int keyTarget = 0; @@ -843,9 +842,8 @@ public class SmsCommunicatorPlugin extends PluginBase { String reply = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration); sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } catch (Exception e) { + } else { sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); - return; } } }); @@ -864,15 +862,9 @@ public class SmsCommunicatorPlugin extends PluginBase { messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { @Override public void run() { - try { - SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); - String reply = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } catch (Exception e) { - e.printStackTrace(); - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); - return; - } + SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); + String reply = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)); + sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); } }); } else From 09c1ab5960d7a127d6f5dfb91da5bbd830018d03 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 11 Nov 2019 14:57:56 +0100 Subject: [PATCH 11/56] smbmaxminutes fix --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c43ba1193..3233a531d8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -814,7 +814,7 @@ BG upload settings Show detailed delta Show delta with one more decimal place - 45 60 75 90 105 120 + SMB max minutes Max minutes of basal to limit SMB to Unsupported pump firmware Send BG data to xDrip+ From 25b18d93d9d7d0f8be804e4d8da33d48f10a5b08 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 11 Nov 2019 16:47:09 +0100 Subject: [PATCH 12/56] fix max_daily_basal to support insight min rate --- .../androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java | 2 +- .../androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java | 2 +- .../androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java index ef31ad599e..65db6e440c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java @@ -164,7 +164,7 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { return; if (!HardLimits.checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!HardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java index 899c0151d8..8cac8b87cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java @@ -162,7 +162,7 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { return; if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java index d410eba291..c27a3f2059 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java @@ -170,7 +170,7 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr return; if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; From 4b7fc10402de38f0fb1ba8a742b79489e9e3ee61 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Mon, 11 Nov 2019 23:00:07 +0000 Subject: [PATCH 13/56] - changed how edit is done.. We now edit Treatment object directly without DetailedBolusInfo object. --- .../medtronic/data/MedtronicHistoryData.java | 67 ++++++++++++------- .../plugins/treatments/TreatmentService.java | 8 +++ 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 1eed440cdc..b11fec645f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -49,6 +49,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpSta import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; import info.nightscout.androidaps.plugins.treatments.Treatment; +import info.nightscout.androidaps.plugins.treatments.TreatmentService; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.SP; @@ -947,36 +948,50 @@ public class MedtronicHistoryData { } else { - DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(treatment.date, bolusDTO.getDeliveredAmount()); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(OldTreatment={}): Bolus={}", treatment, bolusDTO); + + treatment.source = Source.PUMP; + treatment.pumpId = bolus.getPumpId(); + treatment.insulin = bolusDTO.getDeliveredAmount(); + + TreatmentService.UpdateReturn updateReturn = TreatmentsPlugin.getPlugin().getService().createOrUpdateMedtronic(treatment, false); if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: addBolus(tretament={}): Bolus={}, DetailedBolusInfo={}", treatment, bolusDTO, detailedBolusInfo); - - if (detailedBolusInfo == null) { - detailedBolusInfo = new DetailedBolusInfo(); - - if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: detailedBolusInfoCouldNotBeRetrived !"); - } - - detailedBolusInfo.date = treatment.date; - detailedBolusInfo.source = Source.PUMP; - detailedBolusInfo.pumpId = bolus.getPumpId(); - detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); - detailedBolusInfo.carbs = treatment.carbs; - - addCarbsFromEstimate(detailedBolusInfo, bolus); - - if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): DetailedBolusInfo(New)={}", detailedBolusInfo); - - boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); - - bolus.setLinkedObject(detailedBolusInfo); + LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): NewTreatment={}, UpdateReturn={}", treatment, updateReturn); if (isLogEnabled()) - LOG.debug("editBolus - [date={},pumpId={}, insulin={}, newRecord={}]", detailedBolusInfo.date, - detailedBolusInfo.pumpId, detailedBolusInfo.insulin, newRecord); + LOG.debug("editBolus - [date={},pumpId={}, insulin={}, newRecord={}]", treatment.date, + treatment.pumpId, treatment.insulin, updateReturn.toString()); + + bolus.setLinkedObject(treatment); + +// DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(treatment.date, bolusDTO.getDeliveredAmount()); +// +// if (doubleBolusDebug) +// LOG.debug("DoubleBolusDebug: addBolus(tretament={}): Bolus={}, DetailedBolusInfo={}", treatment, bolusDTO, detailedBolusInfo); +// +// if (detailedBolusInfo == null) { +// detailedBolusInfo = new DetailedBolusInfo(); +// +// if (doubleBolusDebug) +// LOG.debug("DoubleBolusDebug: detailedBolusInfoCouldNotBeRetrived !"); +// } +// +// detailedBolusInfo.date = treatment.date; +// detailedBolusInfo.source = Source.PUMP; +// detailedBolusInfo.pumpId = bolus.getPumpId(); +// detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); +// detailedBolusInfo.carbs = treatment.carbs; +// +// addCarbsFromEstimate(detailedBolusInfo, bolus); +// +// if (doubleBolusDebug) +// LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): DetailedBolusInfo(New)={}", detailedBolusInfo); +// +// boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); +// +// bolus.setLinkedObject(detailedBolusInfo); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 7123b6c96b..5c09d609cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -736,6 +736,14 @@ public class TreatmentService extends OrmLiteBaseService { boolean newRecord; boolean success; + + @Override + public String toString() { + return "UpdateReturn [" + + "newRecord=" + newRecord + + ", success=" + success + + ']'; + } } } From b682432ca01fa5bfb8b41818329a2b65c5ed12d0 Mon Sep 17 00:00:00 2001 From: boc-the-git <3479092+boc-the-git@users.noreply.github.com> Date: Tue, 12 Nov 2019 15:39:42 +1100 Subject: [PATCH 14/56] Change roundTo method to use BigDecimal rather than Double for more accurate comparison. Update tests to introduce more scenarios where without the code change, they'd fail. --- .../info/nightscout/androidaps/utils/Round.java | 15 ++++++++++++--- .../nightscout/androidaps/utils/RoundTest.java | 13 ++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/Round.java b/app/src/main/java/info/nightscout/androidaps/utils/Round.java index ba7f7e3f86..3e312a20a9 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/Round.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/Round.java @@ -1,14 +1,23 @@ package info.nightscout.androidaps.utils; +import java.math.BigDecimal; + /** * Created by mike on 20.06.2016. */ public class Round { public static Double roundTo(double x, Double step) { - if (x != 0d) { - return Math.round(x / step) * step; + if (x == 0d) { + return 0d; } - return 0d; + + //Double oldCalc = Math.round(x / step) * step; + Double newCalc = BigDecimal.valueOf(Math.round(x / step)).multiply(BigDecimal.valueOf(step)).doubleValue(); + + // just for the tests, forcing failures + //newCalc = oldCalc; + + return newCalc; } public static Double floorTo(Double x, Double step) { if (x != 0d) { diff --git a/app/src/test/java/info/nightscout/androidaps/utils/RoundTest.java b/app/src/test/java/info/nightscout/androidaps/utils/RoundTest.java index fb444804bc..568b67057e 100644 --- a/app/src/test/java/info/nightscout/androidaps/utils/RoundTest.java +++ b/app/src/test/java/info/nightscout/androidaps/utils/RoundTest.java @@ -12,9 +12,16 @@ public class RoundTest { @Test public void roundToTest() throws Exception { - assertEquals( 0.55d, Round.roundTo(0.54d, 0.05d), 0.00000001d ); - assertEquals( 1d, Round.roundTo(1.49d, 1d), 0.00000001d ); - assertEquals( 0d, Round.roundTo(0d, 1d), 0.00000001d ); + assertEquals( 0.55d, Round.roundTo(0.54d, 0.05d), 0.00000000000000000001d ); + assertEquals( -3.26d, Round.roundTo(-3.2553715764602713d, 0.01d), 0.00000000000000000001d ); + assertEquals( 0.816d, Round.roundTo(0.8156666666666667d, 0.001d), 0.00000000000000000001d ); + assertEquals( 0.235d, Round.roundTo(0.235d, 0.001d), 0.00000000000000000001d ); + assertEquals( 0.3d, Round.roundTo(0.3d, 0.1d), 0.00000000000000001d ); + assertEquals( 0.0017d, Round.roundTo(0.0016960652144170627d, 0.0001d), 0.00000000000000000001d ); + assertEquals( 0.0078d, Round.roundTo(0.007804436682291013d, 0.0001d), 0.00000000000000000001d ); + assertEquals( 0.6d, Round.roundTo(0.6d, 0.05d), 0.00000000000000000001d ); + assertEquals( 1d, Round.roundTo(1.49d, 1d), 0.00000000000000000001d ); + assertEquals( 0d, Round.roundTo(0d, 1d), 0.00000000000000000001d ); } @Test From 37ea3b3e30af56d90cb082da1e16ae2034a4b901 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 12 Nov 2019 07:40:49 +0100 Subject: [PATCH 15/56] better skipping objective message --- app/src/main/res/values/objectives.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/objectives.xml b/app/src/main/res/values/objectives.xml index 469db88006..d5ffbc2e3a 100644 --- a/app/src/main/res/values/objectives.xml +++ b/app/src/main/res/values/objectives.xml @@ -47,7 +47,7 @@ Display content of Loop plugin Use scale function by long-pressing BG chart Enter - Enter code obtained from developers to bypass the rest of objectives + If you were OpenAPS user before and your NS has at least 3 months of looping data, you can send an email to objectives@androidaps.org with your NS address and request code to bypass the rest of objectives. Enter code obtained from developers Code accepted Code invalid Prove your knowledge From cce3a77a37626831375be20b53628644af85e106 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 12 Nov 2019 13:26:03 +0100 Subject: [PATCH 16/56] Log unhandled exception rather than printing to stdout. --- .../androidaps/plugins/aps/loop/APSResult.java | 4 ++-- .../plugins/general/automation/AutomationEvent.java | 7 +++++-- .../plugins/general/automation/actions/Action.java | 9 ++++++--- .../general/automation/actions/ActionLoopSuspend.java | 8 ++++++-- .../general/automation/actions/ActionNotification.java | 8 ++++++-- .../automation/actions/ActionProfileSwitch.java | 4 ++-- .../automation/actions/ActionProfileSwitchPercent.java | 8 ++++++-- .../general/automation/actions/ActionSendSMS.java | 5 +++-- .../automation/actions/ActionStartTempTarget.java | 8 ++++++-- .../automation/actions/ActionStopTempTarget.java | 8 ++++++-- .../plugins/general/automation/triggers/Trigger.java | 7 +++++-- .../automation/triggers/TriggerAutosensValue.java | 4 ++-- .../plugins/general/automation/triggers/TriggerBg.java | 4 ++-- .../general/automation/triggers/TriggerBolusAgo.java | 4 ++-- .../general/automation/triggers/TriggerCOB.java | 4 ++-- .../general/automation/triggers/TriggerConnector.java | 4 ++-- .../general/automation/triggers/TriggerDelta.java | 4 ++-- .../general/automation/triggers/TriggerIob.java | 4 ++-- .../general/automation/triggers/TriggerLocation.java | 4 ++-- .../automation/triggers/TriggerProfilePercent.java | 4 ++-- .../automation/triggers/TriggerPumpLastConnection.java | 4 ++-- .../automation/triggers/TriggerRecurringTime.java | 4 ++-- .../general/automation/triggers/TriggerTempTarget.java | 4 ++-- .../general/automation/triggers/TriggerTime.java | 4 ++-- .../general/automation/triggers/TriggerTimeRange.java | 4 ++-- .../general/automation/triggers/TriggerWifiSsid.java | 4 ++-- .../androidaps/plugins/general/nsclient/NSUpload.java | 2 +- .../rileylink/ble/data/encoding/Encoding4b6bLoop.java | 6 +++--- .../pump/danaR/services/DanaRExecutionService.java | 2 +- .../plugins/pump/danaRS/comm/DanaRS_Packet.java | 6 +++++- .../plugins/pump/danaRS/services/BLEComm.java | 4 ++-- .../plugins/pump/insight/LocalInsightPlugin.java | 4 ++-- .../app_layer/history/history_events/HistoryEvent.java | 9 +++++---- .../pump/medtronic/data/MedtronicHistoryData.java | 3 +-- .../plugins/treatments/TreatmentService.java | 4 ++-- .../nightscout/androidaps/data/QuickWizardTest.java | 2 +- .../general/automation/triggers/TriggerBgTest.java | 2 +- .../general/automation/triggers/TriggerDeltaTest.java | 2 +- .../iob/iobCobCalculatorPlugin/GlucoseStatusTest.java | 10 +++++----- 39 files changed, 113 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java index b3b0c091d5..d802833c38 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java @@ -247,7 +247,7 @@ public class APSResult { } } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return array; } @@ -280,7 +280,7 @@ public class APSResult { } } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return latest; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java index 6eb3be382b..f078386384 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java @@ -10,8 +10,11 @@ import java.util.List; import info.nightscout.androidaps.plugins.general.automation.actions.Action; import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger; import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AutomationEvent { + private static final Logger log = LoggerFactory.getLogger(AutomationEvent.class); private Trigger trigger = new TriggerConnector(); private List actions = new ArrayList<>(); @@ -74,7 +77,7 @@ public class AutomationEvent { } o.put("actions", array); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -91,7 +94,7 @@ public class AutomationEvent { actions.add(Action.instantiate(new JSONObject(array.getString(i)))); } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java index 796f023f1d..78d7ca6844 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java @@ -11,6 +11,8 @@ import javax.annotation.Nullable; import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger; import info.nightscout.androidaps.queue.Callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /* Action ideas: @@ -44,6 +46,7 @@ import info.nightscout.androidaps.queue.Callback; public abstract class Action { + private static final Logger log = LoggerFactory.getLogger(Action.class); public Trigger precondition = null; @@ -65,7 +68,7 @@ public abstract class Action { try { o.put("type", this.getClass().getName()); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -84,7 +87,7 @@ public abstract class Action { Class clazz = Class.forName(type); return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : ""); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return null; } @@ -98,7 +101,7 @@ public abstract class Action { fromJSON(data.toString()); } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java index bd221d8f81..64db429517 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java @@ -18,8 +18,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithE import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionLoopSuspend extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionLoopSuspend.class); + public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @Override @@ -59,7 +63,7 @@ public class ActionLoopSuspend extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -70,7 +74,7 @@ public class ActionLoopSuspend extends Action { JSONObject o = new JSONObject(data); minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes")); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java index 1f7ad543c2..a9d980a229 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java @@ -20,8 +20,12 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionNotification extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionNotification.class); + public InputString text = new InputString(); @Override @@ -59,7 +63,7 @@ public class ActionNotification extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -70,7 +74,7 @@ public class ActionNotification extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java index 50fbb786db..6c05b60b90 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java @@ -92,7 +92,7 @@ public class ActionProfileSwitch extends Action { data.put("profileToSwitchTo", inputProfileName.getValue()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -105,7 +105,7 @@ public class ActionProfileSwitch extends Action { profileName = JsonHelper.safeGetString(d, "profileToSwitchTo"); inputProfileName.setValue(profileName); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java index 20b6abb081..fc1967980a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java @@ -19,8 +19,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuil import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionProfileSwitchPercent extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionProfileSwitchPercent.class); + InputPercent pct = new InputPercent(); InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @@ -71,7 +75,7 @@ public class ActionProfileSwitchPercent extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -83,7 +87,7 @@ public class ActionProfileSwitchPercent extends Action { pct.setValue(JsonHelper.safeGetInt(d, "percentage")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java index f8afafca75..a871114531 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java @@ -21,6 +21,7 @@ import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; public class ActionSendSMS extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionSendSMS.class); public InputString text = new InputString(); @@ -56,7 +57,7 @@ public class ActionSendSMS extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -67,7 +68,7 @@ public class ActionSendSMS extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java index 659bb508eb..4f33a19919 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java @@ -24,8 +24,12 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionStartTempTarget extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionStartTempTarget.class); + String reason = ""; InputTempTarget value = new InputTempTarget(); InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @@ -93,7 +97,7 @@ public class ActionStartTempTarget extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -107,7 +111,7 @@ public class ActionStartTempTarget extends Action { value.setValue(JsonHelper.safeGetDouble(d, "value")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java index de251c499e..f2b5d4a10d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java @@ -14,8 +14,12 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionStopTempTarget extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionStopTempTarget.class); + String reason = ""; private TempTarget tempTarget; @@ -54,7 +58,7 @@ public class ActionStopTempTarget extends Action { data.put("reason", reason); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -65,7 +69,7 @@ public class ActionStopTempTarget extends Action { JSONObject d = new JSONObject(data); reason = JsonHelper.safeGetString(d, "reason"); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java index f6ba6b6f67..7e8e306d6c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java @@ -13,10 +13,13 @@ import com.google.common.base.Optional; import org.json.JSONException; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.annotation.Nullable; public abstract class Trigger { + private static final Logger log = LoggerFactory.getLogger(Trigger.class); TriggerConnector connector = null; long lastRun; @@ -56,7 +59,7 @@ public abstract class Trigger { try { return instantiate(new JSONObject(json)); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return null; } @@ -69,7 +72,7 @@ public abstract class Trigger { Class clazz = Class.forName(type); return ((Trigger) clazz.newInstance()).fromJSON(data.toString()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java index da850b187d..89e2b153bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java @@ -89,7 +89,7 @@ public class TriggerAutosensValue extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -102,7 +102,7 @@ public class TriggerAutosensValue extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java index 1636377bf2..e460447a3e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java @@ -104,7 +104,7 @@ public class TriggerBg extends Trigger { data.put("units", bg.getUnits()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -118,7 +118,7 @@ public class TriggerBg extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java index 0ad9f75ab2..71e3ccc1eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java @@ -86,7 +86,7 @@ public class TriggerBolusAgo extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -99,7 +99,7 @@ public class TriggerBolusAgo extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java index 4ddeb4299a..7d219bd4f0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java @@ -87,7 +87,7 @@ public class TriggerCOB extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -100,7 +100,7 @@ public class TriggerCOB extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java index ae4bd35ad5..f19dad42ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java @@ -162,7 +162,7 @@ public class TriggerConnector extends Trigger { data.put("triggerList", array); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -179,7 +179,7 @@ public class TriggerConnector extends Trigger { add(newItem); } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java index a3939a60b7..55eab5a182 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java @@ -129,7 +129,7 @@ public class TriggerDelta extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -144,7 +144,7 @@ public class TriggerDelta extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java index c4003af861..19eef80572 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java @@ -82,7 +82,7 @@ public class TriggerIob extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerIob extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java index 4772d29112..612d223e55 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java @@ -93,7 +93,7 @@ public class TriggerLocation extends Trigger { data.put("lastRun", lastRun); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -108,7 +108,7 @@ public class TriggerLocation extends Trigger { name.setValue(JsonHelper.safeGetString(d, "name")); lastRun = JsonHelper.safeGetLong(d, "lastRun"); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java index d86bc35cb8..ad94e6dc6d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java @@ -88,7 +88,7 @@ public class TriggerProfilePercent extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -101,7 +101,7 @@ public class TriggerProfilePercent extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java index 8cec3bdf44..2c314aadaa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java @@ -82,7 +82,7 @@ public class TriggerPumpLastConnection extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerPumpLastConnection extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java index a145253b36..eee3ec3ba6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java @@ -163,7 +163,7 @@ public class TriggerRecurringTime extends Trigger { object.put("type", TriggerRecurringTime.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return object.toString(); } @@ -181,7 +181,7 @@ public class TriggerRecurringTime extends Trigger { minute = JsonHelper.safeGetInt(o, "minute"); validTo = JsonHelper.safeGetLong(o, "validTo"); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java index 378652ed2a..331da822e8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java @@ -74,7 +74,7 @@ public class TriggerTempTarget extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -86,7 +86,7 @@ public class TriggerTempTarget extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java index 261783b99c..a2043ef092 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java @@ -65,7 +65,7 @@ public class TriggerTime extends Trigger { object.put("type", TriggerTime.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return object.toString(); } @@ -78,7 +78,7 @@ public class TriggerTime extends Trigger { lastRun = JsonHelper.safeGetLong(o, "lastRun"); runAt = JsonHelper.safeGetLong(o, "runAt"); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java index 8755c3559c..a9f5117dc0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java @@ -93,7 +93,7 @@ public class TriggerTimeRange extends Trigger { object.put("type", TriggerTimeRange.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } log.debug(object.toString()); return object.toString(); @@ -108,7 +108,7 @@ public class TriggerTimeRange extends Trigger { start = JsonHelper.safeGetInt(o, "start"); end = JsonHelper.safeGetInt(o, "end"); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java index ba10e81583..886158c5ef 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java @@ -85,7 +85,7 @@ public class TriggerWifiSsid extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return o.toString(); } @@ -98,7 +98,7 @@ public class TriggerWifiSsid extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java index 881593b32a..708ae31602 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java @@ -594,7 +594,7 @@ public class NSUpload { } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java index 3e551b6ccf..4a2066b25f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java @@ -16,6 +16,7 @@ import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; */ public class Encoding4b6bLoop extends Encoding4b6bAbstract { + private static final Logger log = LoggerFactory.getLogger(Encoding4b6bLoop.class); public static final Logger LOG = LoggerFactory.getLogger(Encoding4b6bLoop.class); public Map codesRev = null; @@ -108,9 +109,8 @@ public class Encoding4b6bLoop extends Encoding4b6bAbstract { int index2 = ((bitAccumulator >> (availBits - 12)) & 0b111111); hiNibble = codesRev.get((bitAccumulator >> (availBits - 6))); loNibble = codesRev.get(((bitAccumulator >> (availBits - 12)) & 0b111111)); - } catch (Exception ex) { - System.out.println("Exception: " + ex.getMessage()); - ex.printStackTrace(); + } catch (Exception e) { + log.debug("Unhandled exception", e); return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java index 0801d8a173..3551fcbe07 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java @@ -383,7 +383,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { try { o.wait(); } catch (InterruptedException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java index 3d59b34e16..752b03ba7e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java @@ -4,11 +4,15 @@ import android.annotation.TargetApi; import android.os.Build; import com.cozmo.danar.util.BleCommandUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.Date; public class DanaRS_Packet { + private static final Logger log = LoggerFactory.getLogger(DanaRS_Packet.class); + protected static final int TYPE_START = 0; protected static final int OPCODE_START = 1; protected static final int DATA_START = 2; @@ -73,7 +77,7 @@ public class DanaRS_Packet { return ret; } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java index 8a43d6e35a..680b0b1f9f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java @@ -551,7 +551,7 @@ public class BLEComm { break; } } catch (Exception e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } startSignatureFound = false; packetIsValid = false; @@ -635,7 +635,7 @@ public class BLEComm { message.wait(5000); } catch (InterruptedException e) { log.error("sendMessage InterruptedException", e); - e.printStackTrace(); + log.debug("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index 7baf9458c9..0d97d4860f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -1526,7 +1526,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con data.put("notes", note); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } @@ -1554,7 +1554,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con data.put("eventType", event); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java index 874c908ca5..671b2eb9d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java @@ -3,8 +3,11 @@ package info.nightscout.androidaps.plugins.pump.insight.app_layer.history.histor import info.nightscout.androidaps.plugins.pump.insight.ids.HistoryEventIDs; import info.nightscout.androidaps.plugins.pump.insight.utils.BOCUtil; import info.nightscout.androidaps.plugins.pump.insight.utils.ByteBuf; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HistoryEvent implements Comparable { + private static final Logger log = LoggerFactory.getLogger(HistoryEvent.class); private int eventYear; private int eventMonth; @@ -22,10 +25,8 @@ public class HistoryEvent implements Comparable { else { try { event = eventClass.newInstance(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); + } catch (IllegalAccessException | InstantiationException e) { + log.debug("Unhandled exception", e); } } event.parseHeader(byteBuf); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 1eed440cdc..a67446000b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -67,7 +67,6 @@ import info.nightscout.androidaps.utils.SP; // All things marked with "TODO: Fix db code" needs to be updated in new 2.5 database code public class MedtronicHistoryData { - private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); private List allHistory = null; @@ -523,7 +522,7 @@ public class MedtronicHistoryData { data.put("eventType", event); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + LOG.debug("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 7123b6c96b..4772077302 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -133,7 +133,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` ADD COLUMN boluscalc STRING;"); } catch (SQLException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } else { if (L.isEnabled(L.DATATREATMENTS)) @@ -147,7 +147,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` DROP COLUMN boluscalc STRING;"); } catch (SQLException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } } diff --git a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java index bda66cc121..0a90106a64 100644 --- a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java +++ b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java @@ -30,7 +30,7 @@ public class QuickWizardTest { try { array = new JSONArray("[" + data1 + "," + data2 + "]"); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java index a090058cd3..cee9116ed3 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java @@ -124,7 +124,7 @@ public class TriggerBgTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":" + (now - 1) + ",\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java index c09af0f5b1..9796790816 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java @@ -152,7 +152,7 @@ public class TriggerDeltaTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java index 19e497b834..6979860459 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java @@ -153,7 +153,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } @@ -165,7 +165,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766800000,\"direction\":\"Flat\"}")))); // +2 list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766600000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } @@ -180,7 +180,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } @@ -190,7 +190,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":1514766900000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } @@ -211,7 +211,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":" + (latest_reading + (i*2)) + ",\"mills\":" + (end_time - (1000 * 60 * i)) + ",\"direction\":\"Flat\"}")))); } } catch (JSONException e) { - e.printStackTrace(); + log.debug("Unhandled exception", e); } return list; } From dc38615ec999ed146a4ee3c12790eed48d43ca24 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 12 Nov 2019 13:44:02 +0100 Subject: [PATCH 17/56] Ignore InternalErrors during app shutdown. --- .../main/java/info/nightscout/androidaps/MainApp.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 379afabddf..168c78be7f 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -129,7 +129,14 @@ public class MainApp extends Application { sConstraintsChecker = new ConstraintChecker(); sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); - Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> log.error("Uncaught exception crashing app", ex)); + Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { + if (ex instanceof InternalError) { + // usually the app trying to spawn a thread while being killed + return; + } + + log.error("Uncaught exception crashing app", ex); + }); try { if (FabricPrivacy.fabricEnabled()) { From b9d600edf45846307e652790df5bae79bb919de0 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 14 Nov 2019 13:57:57 +0100 Subject: [PATCH 18/56] Log unhandled exceptions as errors. --- .../androidaps/plugins/aps/loop/APSResult.java | 4 ++-- .../plugins/general/automation/AutomationEvent.java | 4 ++-- .../plugins/general/automation/actions/Action.java | 6 +++--- .../general/automation/actions/ActionLoopSuspend.java | 4 ++-- .../general/automation/actions/ActionNotification.java | 4 ++-- .../automation/actions/ActionProfileSwitch.java | 4 ++-- .../automation/actions/ActionProfileSwitchPercent.java | 4 ++-- .../general/automation/actions/ActionSendSMS.java | 4 ++-- .../automation/actions/ActionStartTempTarget.java | 4 ++-- .../automation/actions/ActionStopTempTarget.java | 4 ++-- .../plugins/general/automation/triggers/Trigger.java | 4 ++-- .../automation/triggers/TriggerAutosensValue.java | 4 ++-- .../plugins/general/automation/triggers/TriggerBg.java | 4 ++-- .../general/automation/triggers/TriggerBolusAgo.java | 4 ++-- .../general/automation/triggers/TriggerCOB.java | 4 ++-- .../general/automation/triggers/TriggerConnector.java | 4 ++-- .../general/automation/triggers/TriggerDelta.java | 4 ++-- .../general/automation/triggers/TriggerIob.java | 4 ++-- .../general/automation/triggers/TriggerLocation.java | 4 ++-- .../automation/triggers/TriggerProfilePercent.java | 4 ++-- .../automation/triggers/TriggerPumpLastConnection.java | 4 ++-- .../automation/triggers/TriggerRecurringTime.java | 4 ++-- .../general/automation/triggers/TriggerTempTarget.java | 4 ++-- .../general/automation/triggers/TriggerTime.java | 4 ++-- .../general/automation/triggers/TriggerTimeRange.java | 4 ++-- .../general/automation/triggers/TriggerWifiSsid.java | 4 ++-- .../androidaps/plugins/general/nsclient/NSUpload.java | 2 +- .../rileylink/ble/data/encoding/Encoding4b6bLoop.java | 2 +- .../pump/danaR/services/DanaRExecutionService.java | 2 +- .../plugins/pump/danaRS/comm/DanaRS_Packet.java | 2 +- .../plugins/pump/danaRS/services/BLEComm.java | 4 ++-- .../plugins/pump/insight/LocalInsightPlugin.java | 4 ++-- .../app_layer/history/history_events/HistoryEvent.java | 2 +- .../pump/medtronic/data/MedtronicHistoryData.java | 2 +- .../plugins/treatments/TreatmentService.java | 4 ++-- .../nightscout/androidaps/data/QuickWizardTest.java | 2 +- .../general/automation/triggers/TriggerBgTest.java | 2 +- .../general/automation/triggers/TriggerDeltaTest.java | 2 +- .../iob/iobCobCalculatorPlugin/GlucoseStatusTest.java | 10 +++++----- 39 files changed, 73 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java index d802833c38..7e568af2d9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java @@ -247,7 +247,7 @@ public class APSResult { } } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return array; } @@ -280,7 +280,7 @@ public class APSResult { } } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return latest; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java index f078386384..4f7bb62c74 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java @@ -77,7 +77,7 @@ public class AutomationEvent { } o.put("actions", array); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -94,7 +94,7 @@ public class AutomationEvent { actions.add(Action.instantiate(new JSONObject(array.getString(i)))); } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java index 78d7ca6844..ee4564fd6f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java @@ -68,7 +68,7 @@ public abstract class Action { try { o.put("type", this.getClass().getName()); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -87,7 +87,7 @@ public abstract class Action { Class clazz = Class.forName(type); return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : ""); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return null; } @@ -101,7 +101,7 @@ public abstract class Action { fromJSON(data.toString()); } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java index 64db429517..39adbcac27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java @@ -63,7 +63,7 @@ public class ActionLoopSuspend extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -74,7 +74,7 @@ public class ActionLoopSuspend extends Action { JSONObject o = new JSONObject(data); minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes")); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java index a9d980a229..7bd501b460 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java @@ -63,7 +63,7 @@ public class ActionNotification extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -74,7 +74,7 @@ public class ActionNotification extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java index 6c05b60b90..8a4762bd08 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java @@ -92,7 +92,7 @@ public class ActionProfileSwitch extends Action { data.put("profileToSwitchTo", inputProfileName.getValue()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -105,7 +105,7 @@ public class ActionProfileSwitch extends Action { profileName = JsonHelper.safeGetString(d, "profileToSwitchTo"); inputProfileName.setValue(profileName); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java index fc1967980a..23b54640a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java @@ -75,7 +75,7 @@ public class ActionProfileSwitchPercent extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -87,7 +87,7 @@ public class ActionProfileSwitchPercent extends Action { pct.setValue(JsonHelper.safeGetInt(d, "percentage")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java index a871114531..105ba6184e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java @@ -57,7 +57,7 @@ public class ActionSendSMS extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -68,7 +68,7 @@ public class ActionSendSMS extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java index 4f33a19919..b0ef645069 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java @@ -97,7 +97,7 @@ public class ActionStartTempTarget extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -111,7 +111,7 @@ public class ActionStartTempTarget extends Action { value.setValue(JsonHelper.safeGetDouble(d, "value")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java index f2b5d4a10d..abf354bc9c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java @@ -58,7 +58,7 @@ public class ActionStopTempTarget extends Action { data.put("reason", reason); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -69,7 +69,7 @@ public class ActionStopTempTarget extends Action { JSONObject d = new JSONObject(data); reason = JsonHelper.safeGetString(d, "reason"); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java index 7e8e306d6c..69ac67bf07 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java @@ -59,7 +59,7 @@ public abstract class Trigger { try { return instantiate(new JSONObject(json)); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return null; } @@ -72,7 +72,7 @@ public abstract class Trigger { Class clazz = Class.forName(type); return ((Trigger) clazz.newInstance()).fromJSON(data.toString()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java index 89e2b153bf..ce0a8f63bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java @@ -89,7 +89,7 @@ public class TriggerAutosensValue extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -102,7 +102,7 @@ public class TriggerAutosensValue extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java index e460447a3e..3733859b96 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java @@ -104,7 +104,7 @@ public class TriggerBg extends Trigger { data.put("units", bg.getUnits()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -118,7 +118,7 @@ public class TriggerBg extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java index 71e3ccc1eb..0a038112e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java @@ -86,7 +86,7 @@ public class TriggerBolusAgo extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -99,7 +99,7 @@ public class TriggerBolusAgo extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java index 7d219bd4f0..bf17dc72d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java @@ -87,7 +87,7 @@ public class TriggerCOB extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -100,7 +100,7 @@ public class TriggerCOB extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java index f19dad42ac..549925528a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java @@ -162,7 +162,7 @@ public class TriggerConnector extends Trigger { data.put("triggerList", array); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -179,7 +179,7 @@ public class TriggerConnector extends Trigger { add(newItem); } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java index 55eab5a182..eee649307b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java @@ -129,7 +129,7 @@ public class TriggerDelta extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -144,7 +144,7 @@ public class TriggerDelta extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java index 19eef80572..a3935d7cec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java @@ -82,7 +82,7 @@ public class TriggerIob extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerIob extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java index 612d223e55..19965cfc9a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java @@ -93,7 +93,7 @@ public class TriggerLocation extends Trigger { data.put("lastRun", lastRun); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -108,7 +108,7 @@ public class TriggerLocation extends Trigger { name.setValue(JsonHelper.safeGetString(d, "name")); lastRun = JsonHelper.safeGetLong(d, "lastRun"); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java index ad94e6dc6d..e4c0ae4346 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java @@ -88,7 +88,7 @@ public class TriggerProfilePercent extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -101,7 +101,7 @@ public class TriggerProfilePercent extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java index 2c314aadaa..ff8bb335f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java @@ -82,7 +82,7 @@ public class TriggerPumpLastConnection extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerPumpLastConnection extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java index eee3ec3ba6..89c8d17085 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java @@ -163,7 +163,7 @@ public class TriggerRecurringTime extends Trigger { object.put("type", TriggerRecurringTime.class.getName()); object.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return object.toString(); } @@ -181,7 +181,7 @@ public class TriggerRecurringTime extends Trigger { minute = JsonHelper.safeGetInt(o, "minute"); validTo = JsonHelper.safeGetLong(o, "validTo"); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java index 331da822e8..f45efdf4a4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java @@ -74,7 +74,7 @@ public class TriggerTempTarget extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -86,7 +86,7 @@ public class TriggerTempTarget extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java index a2043ef092..08f9a65aa7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java @@ -65,7 +65,7 @@ public class TriggerTime extends Trigger { object.put("type", TriggerTime.class.getName()); object.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return object.toString(); } @@ -78,7 +78,7 @@ public class TriggerTime extends Trigger { lastRun = JsonHelper.safeGetLong(o, "lastRun"); runAt = JsonHelper.safeGetLong(o, "runAt"); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java index a9f5117dc0..1d03871620 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java @@ -93,7 +93,7 @@ public class TriggerTimeRange extends Trigger { object.put("type", TriggerTimeRange.class.getName()); object.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } log.debug(object.toString()); return object.toString(); @@ -108,7 +108,7 @@ public class TriggerTimeRange extends Trigger { start = JsonHelper.safeGetInt(o, "start"); end = JsonHelper.safeGetInt(o, "end"); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java index 886158c5ef..dd2c34bcb8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java @@ -85,7 +85,7 @@ public class TriggerWifiSsid extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return o.toString(); } @@ -98,7 +98,7 @@ public class TriggerWifiSsid extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java index 708ae31602..f5582b71a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java @@ -594,7 +594,7 @@ public class NSUpload { } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java index 4a2066b25f..a7b005b9b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java @@ -110,7 +110,7 @@ public class Encoding4b6bLoop extends Encoding4b6bAbstract { hiNibble = codesRev.get((bitAccumulator >> (availBits - 6))); loNibble = codesRev.get(((bitAccumulator >> (availBits - 12)) & 0b111111)); } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java index 3551fcbe07..dba3b4ca77 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java @@ -383,7 +383,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { try { o.wait(); } catch (InterruptedException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java index 752b03ba7e..55a2e0c51e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java @@ -77,7 +77,7 @@ public class DanaRS_Packet { return ret; } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java index 680b0b1f9f..253b861811 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java @@ -551,7 +551,7 @@ public class BLEComm { break; } } catch (Exception e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } startSignatureFound = false; packetIsValid = false; @@ -635,7 +635,7 @@ public class BLEComm { message.wait(5000); } catch (InterruptedException e) { log.error("sendMessage InterruptedException", e); - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index 0d97d4860f..5104e70fe8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -1526,7 +1526,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con data.put("notes", note); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } @@ -1554,7 +1554,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con data.put("eventType", event); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java index 671b2eb9d1..f6b3dc588c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java @@ -26,7 +26,7 @@ public class HistoryEvent implements Comparable { try { event = eventClass.newInstance(); } catch (IllegalAccessException | InstantiationException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } event.parseHeader(byteBuf); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index a67446000b..1aa4ea2e1e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -522,7 +522,7 @@ public class MedtronicHistoryData { data.put("eventType", event); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - LOG.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 4772077302..5f67293427 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -133,7 +133,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` ADD COLUMN boluscalc STRING;"); } catch (SQLException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } else { if (L.isEnabled(L.DATATREATMENTS)) @@ -147,7 +147,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` DROP COLUMN boluscalc STRING;"); } catch (SQLException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } } diff --git a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java index 0a90106a64..810ebca5db 100644 --- a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java +++ b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java @@ -30,7 +30,7 @@ public class QuickWizardTest { try { array = new JSONArray("[" + data1 + "," + data2 + "]"); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java index cee9116ed3..bd83e63eee 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java @@ -124,7 +124,7 @@ public class TriggerBgTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":" + (now - 1) + ",\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java index 9796790816..296b41f3d7 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java @@ -152,7 +152,7 @@ public class TriggerDeltaTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java index 6979860459..ccba31aa94 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java @@ -153,7 +153,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } @@ -165,7 +165,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766800000,\"direction\":\"Flat\"}")))); // +2 list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766600000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } @@ -180,7 +180,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } @@ -190,7 +190,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":1514766900000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } @@ -211,7 +211,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":" + (latest_reading + (i*2)) + ",\"mills\":" + (end_time - (1000 * 60 * i)) + ",\"direction\":\"Flat\"}")))); } } catch (JSONException e) { - log.debug("Unhandled exception", e); + log.error("Unhandled exception", e); } return list; } From 94f10a44a50a6a3fe06e42a33a63fcf553b17ee9 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 14 Nov 2019 18:04:36 +0100 Subject: [PATCH 19/56] Rethrow unhandled exceptions in tests as runtime exception. --- .../nightscout/androidaps/data/QuickWizardTest.java | 2 +- .../general/automation/triggers/TriggerBgTest.java | 2 +- .../general/automation/triggers/TriggerDeltaTest.java | 2 +- .../iob/iobCobCalculatorPlugin/GlucoseStatusTest.java | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java index 810ebca5db..820e8d34b1 100644 --- a/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java +++ b/app/src/test/java/info/nightscout/androidaps/data/QuickWizardTest.java @@ -30,7 +30,7 @@ public class QuickWizardTest { try { array = new JSONArray("[" + data1 + "," + data2 + "]"); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java index bd83e63eee..1fb94492c9 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBgTest.java @@ -124,7 +124,7 @@ public class TriggerBgTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":" + (now - 1) + ",\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java index 296b41f3d7..3d36968cec 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDeltaTest.java @@ -152,7 +152,7 @@ public class TriggerDeltaTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java index ccba31aa94..6d91bea911 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/iob/iobCobCalculatorPlugin/GlucoseStatusTest.java @@ -153,7 +153,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":226,\"mills\":1514765100000,\"direction\":\"Flat\"}")))); list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } @@ -165,7 +165,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766800000,\"direction\":\"Flat\"}")))); // +2 list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":216,\"mills\":1514766600000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } @@ -180,7 +180,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":228,\"mills\":1514764800000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } @@ -190,7 +190,7 @@ public class GlucoseStatusTest { try { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":1514766900000,\"direction\":\"Flat\"}")))); } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } @@ -211,7 +211,7 @@ public class GlucoseStatusTest { list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":" + (latest_reading + (i*2)) + ",\"mills\":" + (end_time - (1000 * 60 * i)) + ",\"direction\":\"Flat\"}")))); } } catch (JSONException e) { - log.error("Unhandled exception", e); + throw new RuntimeException(e); } return list; } From db403dc39db27682dcb89c4e309e043fc1e5b980 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 14 Nov 2019 20:49:29 +0100 Subject: [PATCH 20/56] MDT logging fixup. --- .../plugins/pump/medtronic/data/MedtronicHistoryData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 1aa4ea2e1e..3b991c800b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -522,7 +522,7 @@ public class MedtronicHistoryData { data.put("eventType", event); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - log.error("Unhandled exception", e); + LOG.error("Unhandled exception", e); } } From 740b795b425ca7b3e0d23a91bddcb217eefa1855 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Thu, 14 Nov 2019 21:22:03 +0000 Subject: [PATCH 21/56] Little bit of cleanup: - removed some unsused (commented) code - removed few logs that were very useless --- .../pump/medtronic/MedtronicPumpPlugin.java | 1 - .../pump/MedtronicPumpHistoryDecoder.java | 9 +------ .../comm/history/pump/PumpHistoryResult.java | 21 +++------------ .../medtronic/data/MedtronicHistoryData.java | 27 ------------------- 4 files changed, 5 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index f5b882eaf2..f0d10e0370 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -873,7 +873,6 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter detailedBolusInfo.deliverAt = now; // not sure about that one TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); - DetailedBolusInfoStorage.INSTANCE.add(detailedBolusInfo); // we subtract insulin, exact amount will be visible with next remainingInsulin update. getMDTPumpStatus().reservoirRemainingUnits -= detailedBolusInfo.insulin; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java index 32ca320de2..71f387d176 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java @@ -412,25 +412,18 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder validEntries; - // private Object validValues; - public PumpHistoryResult(PumpHistoryEntry searchEntry, Long targetDate) { if (searchEntry != null) { /* @@ -109,9 +105,8 @@ public class PumpHistoryResult { if (unprocessedEntry.isAfter(this.searchDate)) { this.validEntries.add(unprocessedEntry); } else { - LOG.debug("PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", - DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); - +// LOG.debug("PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", +// DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); if (DateTimeUtil.getYear(unprocessedEntry.atechDateTime) > 2015) olderEntries++; } @@ -131,14 +126,6 @@ public class PumpHistoryResult { } - private void clearOrPrepareList() { - if (this.validEntries == null) - this.validEntries = new ArrayList<>(); - else - this.validEntries.clear(); - } - - public String toString() { return "PumpHistoryResult [unprocessed=" + (unprocessedEntries != null ? "" + unprocessedEntries.size() : "0") + // ", valid=" + (validEntries != null ? "" + validEntries.size() : "0") + // diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index f56d3a3cca..db642d15e6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -979,33 +979,6 @@ public class MedtronicHistoryData { bolus.setLinkedObject(treatment); -// DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(treatment.date, bolusDTO.getDeliveredAmount()); -// -// if (doubleBolusDebug) -// LOG.debug("DoubleBolusDebug: addBolus(tretament={}): Bolus={}, DetailedBolusInfo={}", treatment, bolusDTO, detailedBolusInfo); -// -// if (detailedBolusInfo == null) { -// detailedBolusInfo = new DetailedBolusInfo(); -// -// if (doubleBolusDebug) -// LOG.debug("DoubleBolusDebug: detailedBolusInfoCouldNotBeRetrived !"); -// } -// -// detailedBolusInfo.date = treatment.date; -// detailedBolusInfo.source = Source.PUMP; -// detailedBolusInfo.pumpId = bolus.getPumpId(); -// detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); -// detailedBolusInfo.carbs = treatment.carbs; -// -// addCarbsFromEstimate(detailedBolusInfo, bolus); -// -// if (doubleBolusDebug) -// LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): DetailedBolusInfo(New)={}", detailedBolusInfo); -// -// boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); -// -// bolus.setLinkedObject(detailedBolusInfo); - } } From 11665a1d89c46f4cdc478a55150d48091a4f5569 Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Thu, 7 Nov 2019 22:39:29 +0100 Subject: [PATCH 22/56] [#728] Complication support - Watch OS selection of Short, Long and Image complications with current AAPS status (SGV, trend, delta, BR, IoB, CoB, phone/uploader battery) --- .../battery-charging-wireless-10-burnin.svg | 4 + .../battery-charging-wireless-20-burnin.svg | 4 + .../battery-charging-wireless-30-burnin.svg | 4 + .../battery-charging-wireless-40-burnin.svg | 4 + .../battery-charging-wireless-50-burnin.svg | 4 + .../battery-charging-wireless-60-burnin.svg | 4 + .../battery-charging-wireless-70-burnin.svg | 4 + .../battery-charging-wireless-80-burnin.svg | 4 + .../battery-charging-wireless-90-burnin.svg | 4 + .../battery-charging-wireless-burnin.svg | 4 + .../battery-burnin/battery-unknown-burnin.svg | 4 + .../mask-burnin-battery-raw.svg | 55 +++ icons/battery-source/mask-burnin-battery.svg | 223 ++++++++++ .../battery/battery-charging-wireless-10.svg | 1 + .../battery/battery-charging-wireless-20.svg | 1 + .../battery/battery-charging-wireless-30.svg | 1 + .../battery/battery-charging-wireless-40.svg | 1 + .../battery/battery-charging-wireless-50.svg | 1 + .../battery/battery-charging-wireless-60.svg | 1 + .../battery/battery-charging-wireless-70.svg | 1 + .../battery/battery-charging-wireless-80.svg | 1 + .../battery/battery-charging-wireless-90.svg | 1 + icons/battery/battery-charging-wireless.svg | 1 + icons/battery/battery-outline.svg | 1 + icons/battery/battery-unknown.svg | 1 + .../ic_br_cob_iob_orig.svg | 93 ++++ .../ic_cob_detailed_orig.svg | 83 ++++ .../complications-source/ic_cob_iob_orig.svg | 83 ++++ .../ic_ins_burnin_orig.svg | 57 +++ icons/complications-source/ic_ins_orig.svg | 56 +++ .../ic_iob_detailed_orig.svg | 88 ++++ icons/complications/ic_aaps_full.svg | 36 ++ icons/complications/ic_basal.svg | 35 ++ icons/complications/ic_br_cob_iob.svg | 40 ++ icons/complications/ic_carbs.svg | 35 ++ icons/complications/ic_cob_detailed.svg | 36 ++ icons/complications/ic_cob_iob.svg | 36 ++ icons/complications/ic_ins.svg | 31 ++ icons/complications/ic_ins_burnin.svg | 31 ++ icons/complications/ic_iob_detailed.svg | 36 ++ icons/complications/ic_sgv.svg | 35 ++ wear/build.gradle | 4 + wear/src/main/AndroidManifest.xml | 224 ++++++++++ wear/src/main/assets/watch_dark.jpg | Bin 0 -> 21801 bytes wear/src/main/assets/watch_gray.jpg | Bin 0 -> 19759 bytes wear/src/main/assets/watch_light.jpg | Bin 0 -> 19708 bytes .../java/info/nightscout/androidaps/aaps.java | 75 ++++ .../BaseComplicationProviderService.java | 415 ++++++++++++++++++ .../complications/BrCobIobComplication.java | 49 +++ .../CobDetailedComplication.java | 52 +++ .../complications/CobIconComplication.java | 51 +++ .../complications/CobIobComplication.java | 46 ++ .../complications/ComplicationAction.java | 12 + .../ComplicationTapBroadcastReceiver.java | 167 +++++++ .../IobDetailedComplication.java | 52 +++ .../complications/IobIconComplication.java | 56 +++ .../complications/LongStatusComplication.java | 52 +++ .../LongStatusFlippedComplication.java | 53 +++ .../complications/SgvComplication.java | 48 ++ .../complications/UploaderBattery.java | 114 +++++ .../complications/WallpaperComplication.java | 62 +++ .../WallpaperDarkComplication.java | 22 + .../WallpaperGrayComplication.java | 22 + .../WallpaperLightComplication.java | 22 + .../androidaps/data/DisplayRawData.java | 269 ++++++++++++ .../androidaps/data/ListenerService.java | 4 + .../interaction/utils/Constants.java | 14 + .../interaction/utils/DisplayFormat.java | 129 ++++++ .../interaction/utils/Inevitable.java | 117 +++++ .../interaction/utils/Persistence.java | 94 ++++ .../utils/SmallestDoubleString.java | 130 ++++++ .../interaction/utils/WearUtil.java | 110 ++++- .../androidaps/watchfaces/BaseWatchFace.java | 226 ++-------- .../androidaps/watchfaces/BgGraphBuilder.java | 37 ++ .../androidaps/watchfaces/Cockpit.java | 8 +- .../androidaps/watchfaces/Home.java | 16 +- .../androidaps/watchfaces/Home2.java | 18 +- .../androidaps/watchfaces/LargeHome.java | 22 +- .../androidaps/watchfaces/Steampunk.java | 34 +- wear/src/main/res/drawable/ic_aaps_dark.xml | 12 + wear/src/main/res/drawable/ic_aaps_full.xml | 12 + wear/src/main/res/drawable/ic_aaps_gray.xml | 12 + wear/src/main/res/drawable/ic_aaps_light.xml | 12 + wear/src/main/res/drawable/ic_alert.xml | 9 + .../src/main/res/drawable/ic_alert_burnin.xml | 9 + .../ic_battery_alert_variant_outline.xml | 9 + .../drawable/ic_battery_charging_wireless.xml | 9 + .../ic_battery_charging_wireless_10.xml | 9 + ...ic_battery_charging_wireless_10_burnin.xml | 10 + .../ic_battery_charging_wireless_20.xml | 9 + ...ic_battery_charging_wireless_20_burnin.xml | 10 + .../ic_battery_charging_wireless_30.xml | 9 + ...ic_battery_charging_wireless_30_burnin.xml | 10 + .../ic_battery_charging_wireless_40.xml | 9 + ...ic_battery_charging_wireless_40_burnin.xml | 10 + .../ic_battery_charging_wireless_50.xml | 9 + ...ic_battery_charging_wireless_50_burnin.xml | 10 + .../ic_battery_charging_wireless_60.xml | 9 + ...ic_battery_charging_wireless_60_burnin.xml | 10 + .../ic_battery_charging_wireless_70.xml | 9 + ...ic_battery_charging_wireless_70_burnin.xml | 10 + .../ic_battery_charging_wireless_80.xml | 9 + ...ic_battery_charging_wireless_80_burnin.xml | 10 + .../ic_battery_charging_wireless_90.xml | 9 + ...ic_battery_charging_wireless_90_burnin.xml | 10 + .../ic_battery_charging_wireless_burnin.xml | 10 + .../ic_battery_charging_wireless_outline.xml | 9 + .../main/res/drawable/ic_battery_outline.xml | 9 + .../main/res/drawable/ic_battery_unknown.xml | 9 + .../drawable/ic_battery_unknown_burnin.xml | 10 + wear/src/main/res/drawable/ic_br_cob_iob.xml | 11 + wear/src/main/res/drawable/ic_carbs.xml | 11 + .../src/main/res/drawable/ic_cob_detailed.xml | 11 + wear/src/main/res/drawable/ic_cob_iob.xml | 11 + wear/src/main/res/drawable/ic_ins.xml | 11 + wear/src/main/res/drawable/ic_ins_burnin.xml | 11 + .../src/main/res/drawable/ic_iob_detailed.xml | 11 + wear/src/main/res/drawable/ic_sgv.xml | 13 + wear/src/main/res/drawable/ic_sync_alert.xml | 9 + wear/src/main/res/values/strings.xml | 28 ++ wear/src/main/res/xml/preferences.xml | 14 + 121 files changed, 4181 insertions(+), 233 deletions(-) create mode 100644 icons/battery-burnin/battery-charging-wireless-10-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-20-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-30-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-40-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-50-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-60-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-70-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-80-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-90-burnin.svg create mode 100644 icons/battery-burnin/battery-charging-wireless-burnin.svg create mode 100644 icons/battery-burnin/battery-unknown-burnin.svg create mode 100644 icons/battery-source/mask-burnin-battery-raw.svg create mode 100644 icons/battery-source/mask-burnin-battery.svg create mode 100644 icons/battery/battery-charging-wireless-10.svg create mode 100644 icons/battery/battery-charging-wireless-20.svg create mode 100644 icons/battery/battery-charging-wireless-30.svg create mode 100644 icons/battery/battery-charging-wireless-40.svg create mode 100644 icons/battery/battery-charging-wireless-50.svg create mode 100644 icons/battery/battery-charging-wireless-60.svg create mode 100644 icons/battery/battery-charging-wireless-70.svg create mode 100644 icons/battery/battery-charging-wireless-80.svg create mode 100644 icons/battery/battery-charging-wireless-90.svg create mode 100644 icons/battery/battery-charging-wireless.svg create mode 100644 icons/battery/battery-outline.svg create mode 100644 icons/battery/battery-unknown.svg create mode 100644 icons/complications-source/ic_br_cob_iob_orig.svg create mode 100644 icons/complications-source/ic_cob_detailed_orig.svg create mode 100644 icons/complications-source/ic_cob_iob_orig.svg create mode 100644 icons/complications-source/ic_ins_burnin_orig.svg create mode 100644 icons/complications-source/ic_ins_orig.svg create mode 100644 icons/complications-source/ic_iob_detailed_orig.svg create mode 100644 icons/complications/ic_aaps_full.svg create mode 100644 icons/complications/ic_basal.svg create mode 100644 icons/complications/ic_br_cob_iob.svg create mode 100644 icons/complications/ic_carbs.svg create mode 100644 icons/complications/ic_cob_detailed.svg create mode 100644 icons/complications/ic_cob_iob.svg create mode 100644 icons/complications/ic_ins.svg create mode 100644 icons/complications/ic_ins_burnin.svg create mode 100644 icons/complications/ic_iob_detailed.svg create mode 100644 icons/complications/ic_sgv.svg create mode 100644 wear/src/main/assets/watch_dark.jpg create mode 100644 wear/src/main/assets/watch_gray.jpg create mode 100644 wear/src/main/assets/watch_light.jpg create mode 100644 wear/src/main/java/info/nightscout/androidaps/aaps.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/ComplicationAction.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/ComplicationTapBroadcastReceiver.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/WallpaperDarkComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/WallpaperGrayComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/complications/WallpaperLightComplication.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/Constants.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java create mode 100644 wear/src/main/res/drawable/ic_aaps_dark.xml create mode 100644 wear/src/main/res/drawable/ic_aaps_full.xml create mode 100644 wear/src/main/res/drawable/ic_aaps_gray.xml create mode 100644 wear/src/main/res/drawable/ic_aaps_light.xml create mode 100644 wear/src/main/res/drawable/ic_alert.xml create mode 100644 wear/src/main/res/drawable/ic_alert_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_alert_variant_outline.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_10.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_10_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_20.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_20_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_30.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_30_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_40.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_40_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_50.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_50_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_60.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_60_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_70.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_70_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_80.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_80_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_90.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_90_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_battery_charging_wireless_outline.xml create mode 100644 wear/src/main/res/drawable/ic_battery_outline.xml create mode 100644 wear/src/main/res/drawable/ic_battery_unknown.xml create mode 100644 wear/src/main/res/drawable/ic_battery_unknown_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_br_cob_iob.xml create mode 100644 wear/src/main/res/drawable/ic_carbs.xml create mode 100644 wear/src/main/res/drawable/ic_cob_detailed.xml create mode 100644 wear/src/main/res/drawable/ic_cob_iob.xml create mode 100644 wear/src/main/res/drawable/ic_ins.xml create mode 100644 wear/src/main/res/drawable/ic_ins_burnin.xml create mode 100644 wear/src/main/res/drawable/ic_iob_detailed.xml create mode 100644 wear/src/main/res/drawable/ic_sgv.xml create mode 100644 wear/src/main/res/drawable/ic_sync_alert.xml diff --git a/icons/battery-burnin/battery-charging-wireless-10-burnin.svg b/icons/battery-burnin/battery-charging-wireless-10-burnin.svg new file mode 100644 index 0000000000..2888abf209 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-10-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-20-burnin.svg b/icons/battery-burnin/battery-charging-wireless-20-burnin.svg new file mode 100644 index 0000000000..77d52751d7 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-20-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-30-burnin.svg b/icons/battery-burnin/battery-charging-wireless-30-burnin.svg new file mode 100644 index 0000000000..9ffa356436 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-30-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-40-burnin.svg b/icons/battery-burnin/battery-charging-wireless-40-burnin.svg new file mode 100644 index 0000000000..d9ccc46027 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-40-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-50-burnin.svg b/icons/battery-burnin/battery-charging-wireless-50-burnin.svg new file mode 100644 index 0000000000..4d4905046c --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-50-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-60-burnin.svg b/icons/battery-burnin/battery-charging-wireless-60-burnin.svg new file mode 100644 index 0000000000..e3c4ba5c44 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-60-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-70-burnin.svg b/icons/battery-burnin/battery-charging-wireless-70-burnin.svg new file mode 100644 index 0000000000..fe26b5e93d --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-70-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-80-burnin.svg b/icons/battery-burnin/battery-charging-wireless-80-burnin.svg new file mode 100644 index 0000000000..c949afb1c8 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-80-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-90-burnin.svg b/icons/battery-burnin/battery-charging-wireless-90-burnin.svg new file mode 100644 index 0000000000..13677dc328 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-90-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-charging-wireless-burnin.svg b/icons/battery-burnin/battery-charging-wireless-burnin.svg new file mode 100644 index 0000000000..f074b89d34 --- /dev/null +++ b/icons/battery-burnin/battery-charging-wireless-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-burnin/battery-unknown-burnin.svg b/icons/battery-burnin/battery-unknown-burnin.svg new file mode 100644 index 0000000000..b0cc3bba40 --- /dev/null +++ b/icons/battery-burnin/battery-unknown-burnin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/battery-source/mask-burnin-battery-raw.svg b/icons/battery-source/mask-burnin-battery-raw.svg new file mode 100644 index 0000000000..e2ab79f2a1 --- /dev/null +++ b/icons/battery-source/mask-burnin-battery-raw.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/battery-source/mask-burnin-battery.svg b/icons/battery-source/mask-burnin-battery.svg new file mode 100644 index 0000000000..09be5da8a9 --- /dev/null +++ b/icons/battery-source/mask-burnin-battery.svg @@ -0,0 +1,223 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/battery/battery-charging-wireless-10.svg b/icons/battery/battery-charging-wireless-10.svg new file mode 100644 index 0000000000..d6dbd7febc --- /dev/null +++ b/icons/battery/battery-charging-wireless-10.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-20.svg b/icons/battery/battery-charging-wireless-20.svg new file mode 100644 index 0000000000..9e4badedc9 --- /dev/null +++ b/icons/battery/battery-charging-wireless-20.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-30.svg b/icons/battery/battery-charging-wireless-30.svg new file mode 100644 index 0000000000..7da87ce966 --- /dev/null +++ b/icons/battery/battery-charging-wireless-30.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-40.svg b/icons/battery/battery-charging-wireless-40.svg new file mode 100644 index 0000000000..b9aaad2b0f --- /dev/null +++ b/icons/battery/battery-charging-wireless-40.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-50.svg b/icons/battery/battery-charging-wireless-50.svg new file mode 100644 index 0000000000..705a61c55b --- /dev/null +++ b/icons/battery/battery-charging-wireless-50.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-60.svg b/icons/battery/battery-charging-wireless-60.svg new file mode 100644 index 0000000000..b2cd9f7734 --- /dev/null +++ b/icons/battery/battery-charging-wireless-60.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-70.svg b/icons/battery/battery-charging-wireless-70.svg new file mode 100644 index 0000000000..608a404882 --- /dev/null +++ b/icons/battery/battery-charging-wireless-70.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-80.svg b/icons/battery/battery-charging-wireless-80.svg new file mode 100644 index 0000000000..c604743cc3 --- /dev/null +++ b/icons/battery/battery-charging-wireless-80.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless-90.svg b/icons/battery/battery-charging-wireless-90.svg new file mode 100644 index 0000000000..246886ad08 --- /dev/null +++ b/icons/battery/battery-charging-wireless-90.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-charging-wireless.svg b/icons/battery/battery-charging-wireless.svg new file mode 100644 index 0000000000..b36143c4c2 --- /dev/null +++ b/icons/battery/battery-charging-wireless.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-outline.svg b/icons/battery/battery-outline.svg new file mode 100644 index 0000000000..e05e71b288 --- /dev/null +++ b/icons/battery/battery-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/battery/battery-unknown.svg b/icons/battery/battery-unknown.svg new file mode 100644 index 0000000000..8e117be5cb --- /dev/null +++ b/icons/battery/battery-unknown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/complications-source/ic_br_cob_iob_orig.svg b/icons/complications-source/ic_br_cob_iob_orig.svg new file mode 100644 index 0000000000..f650e12d99 --- /dev/null +++ b/icons/complications-source/ic_br_cob_iob_orig.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/complications-source/ic_cob_detailed_orig.svg b/icons/complications-source/ic_cob_detailed_orig.svg new file mode 100644 index 0000000000..e599774c5d --- /dev/null +++ b/icons/complications-source/ic_cob_detailed_orig.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/complications-source/ic_cob_iob_orig.svg b/icons/complications-source/ic_cob_iob_orig.svg new file mode 100644 index 0000000000..98c1764554 --- /dev/null +++ b/icons/complications-source/ic_cob_iob_orig.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/complications-source/ic_ins_burnin_orig.svg b/icons/complications-source/ic_ins_burnin_orig.svg new file mode 100644 index 0000000000..c5bcdfe0ff --- /dev/null +++ b/icons/complications-source/ic_ins_burnin_orig.svg @@ -0,0 +1,57 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/icons/complications-source/ic_ins_orig.svg b/icons/complications-source/ic_ins_orig.svg new file mode 100644 index 0000000000..7601867f58 --- /dev/null +++ b/icons/complications-source/ic_ins_orig.svg @@ -0,0 +1,56 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/icons/complications-source/ic_iob_detailed_orig.svg b/icons/complications-source/ic_iob_detailed_orig.svg new file mode 100644 index 0000000000..3479a78565 --- /dev/null +++ b/icons/complications-source/ic_iob_detailed_orig.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/complications/ic_aaps_full.svg b/icons/complications/ic_aaps_full.svg new file mode 100644 index 0000000000..4e005a8228 --- /dev/null +++ b/icons/complications/ic_aaps_full.svg @@ -0,0 +1,36 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_basal.svg b/icons/complications/ic_basal.svg new file mode 100644 index 0000000000..0491265177 --- /dev/null +++ b/icons/complications/ic_basal.svg @@ -0,0 +1,35 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_br_cob_iob.svg b/icons/complications/ic_br_cob_iob.svg new file mode 100644 index 0000000000..b340c42187 --- /dev/null +++ b/icons/complications/ic_br_cob_iob.svg @@ -0,0 +1,40 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/complications/ic_carbs.svg b/icons/complications/ic_carbs.svg new file mode 100644 index 0000000000..0785741d47 --- /dev/null +++ b/icons/complications/ic_carbs.svg @@ -0,0 +1,35 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_cob_detailed.svg b/icons/complications/ic_cob_detailed.svg new file mode 100644 index 0000000000..5132260c3c --- /dev/null +++ b/icons/complications/ic_cob_detailed.svg @@ -0,0 +1,36 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_cob_iob.svg b/icons/complications/ic_cob_iob.svg new file mode 100644 index 0000000000..81a2e9250f --- /dev/null +++ b/icons/complications/ic_cob_iob.svg @@ -0,0 +1,36 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_ins.svg b/icons/complications/ic_ins.svg new file mode 100644 index 0000000000..e5d1b3e147 --- /dev/null +++ b/icons/complications/ic_ins.svg @@ -0,0 +1,31 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/icons/complications/ic_ins_burnin.svg b/icons/complications/ic_ins_burnin.svg new file mode 100644 index 0000000000..623db41e5b --- /dev/null +++ b/icons/complications/ic_ins_burnin.svg @@ -0,0 +1,31 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/icons/complications/ic_iob_detailed.svg b/icons/complications/ic_iob_detailed.svg new file mode 100644 index 0000000000..8bd9aabc0e --- /dev/null +++ b/icons/complications/ic_iob_detailed.svg @@ -0,0 +1,36 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/complications/ic_sgv.svg b/icons/complications/ic_sgv.svg new file mode 100644 index 0000000000..1dc46d403b --- /dev/null +++ b/icons/complications/ic_sgv.svg @@ -0,0 +1,35 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/wear/build.gradle b/wear/build.gradle index 7e6500fe56..8d4dd71af3 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -92,6 +92,10 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') //implementation files("libs/hellocharts-library-1.5.5.jar") //compile "com.ustwo.android:clockwise-wearable:1.0.2" + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.legacy:legacy-support-v13:1.0.0' + compileOnly "com.google.android.wearable:wearable:${wearableVersion}" implementation "com.google.android.support:wearable:${wearableVersion}" implementation "com.google.android.gms:play-services-wearable:${playServicesWearable}" diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index 874a9c8763..2f89781293 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wear/src/main/assets/watch_dark.jpg b/wear/src/main/assets/watch_dark.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54cd4dc1dc79f34c55857323696c048e33613414 GIT binary patch literal 21801 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

v;t;JEa-hkx&vXO2_SjwYqYCpDLvKj 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 literal 0 HcmV?d00001 diff --git a/wear/src/main/assets/watch_light.jpg b/wear/src/main/assets/watch_light.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a8183304d62c37b7c04ac51111c2005a756694da GIT binary patch literal 19708 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 23; + } + + public static String gs(@StringRes final int id) { + return getAppContext().getString(id); + } + + public static String gs(@StringRes final int id, String... args) { + return getAppContext().getString(id, (Object[]) args); + } + + public static Boolean areComplicationsUnicode() { + return unicodeComplications; + } + + public static String getComplicationTapAction() { + return complicationTapAction; + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + updatePrefs(sharedPrefs); + + // we trigger update on Complications + Intent messageIntent = new Intent(); + messageIntent.setAction(Intent.ACTION_SEND); + LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java new file mode 100644 index 0000000000..c1f33e41c5 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java @@ -0,0 +1,415 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.drawable.Icon; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.complications.ComplicationManager; +import android.support.wearable.complications.ComplicationProviderService; +import android.support.wearable.complications.ComplicationText; +import android.support.wearable.complications.ProviderUpdateRequester; +import android.util.Log; + +import java.util.HashSet; +import java.util.Set; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.ListenerService; +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; + +/** + * Base class for all complications + * + * Created by dlvoy on 2019-11-12 + */ +public abstract class BaseComplicationProviderService extends ComplicationProviderService { + + private static final String TAG = BaseComplicationProviderService.class.getSimpleName(); + + private static final String KEY_COMPLICATIONS = "complications"; + private static final String KEY_LAST_SINCE = "lastSince"; + private static final String KEY_STALE_REPORTED = "staleReported"; + private static final String TASK_ID_REFRESH_COMPLICATION = "refresh-complication"; + + + private LocalBroadcastManager localBroadcastManager; + private MessageReceiver messageReceiver; + + public static void turnOff() { + Log.d(TAG, "TURNING OFF all active complications"); + final Persistence persistence = new Persistence(); + persistence.putString(KEY_COMPLICATIONS, ""); + } + + //============================================================================================== + // ABSTRACT COMPLICATION INTERFACE + //============================================================================================== + + public abstract ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent); + public abstract String getProviderCanonicalName(); + + public ComplicationAction getComplicationAction() { return ComplicationAction.MENU; }; + + //---------------------------------------------------------------------------------------------- + // DEFAULT BEHAVIOURS + //---------------------------------------------------------------------------------------------- + + public ComplicationData buildNoSyncComplicationData(int dataType, + DisplayRawData raw, + PendingIntent complicationPendingIntent, + PendingIntent exceptionalPendingIntent, + long since) { + ComplicationData complicationData = null; + + final ComplicationData.Builder builder = new ComplicationData.Builder(dataType); + if (dataType != ComplicationData.TYPE_LARGE_IMAGE) { + builder.setIcon(Icon.createWithResource(this, R.drawable.ic_sync_alert)); + } + + if (dataType == ComplicationData.TYPE_RANGED_VALUE) { + builder.setMinValue(0); + builder.setMaxValue(100); + builder.setValue(0); + } + + switch (dataType) { + case ComplicationData.TYPE_ICON: + case ComplicationData.TYPE_SHORT_TEXT: + case ComplicationData.TYPE_RANGED_VALUE: + if (since > 0) { + builder.setShortText(ComplicationText.plainText(DisplayFormat.shortTimeSince(since) + " old")); + } else { + builder.setShortText(ComplicationText.plainText("!err!")); + } + break; + case ComplicationData.TYPE_LONG_TEXT: + builder.setLongTitle(ComplicationText.plainText(aaps.gs(R.string.label_warning_sync))); + if (since > 0) { + builder.setLongText(ComplicationText.plainText(String.format(aaps.gs(R.string.label_warning_since), DisplayFormat.shortTimeSince(since)))); + } else { + builder.setLongText(ComplicationText.plainText(aaps.gs(R.string.label_warning_sync_aaps))); + } + break; + case ComplicationData.TYPE_LARGE_IMAGE: + return buildComplicationData(dataType, raw, complicationPendingIntent); + default: + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + break; + } + + builder.setTapAction(exceptionalPendingIntent); + complicationData = builder.build(); + return complicationData; + } + + public ComplicationData buildOutdatedComplicationData(int dataType, + DisplayRawData raw, + PendingIntent complicationPendingIntent, + PendingIntent exceptionalPendingIntent, + long since) { + ComplicationData complicationData = null; + + final ComplicationData.Builder builder = new ComplicationData.Builder(dataType); + if (dataType != ComplicationData.TYPE_LARGE_IMAGE) { + builder.setIcon(Icon.createWithResource(this, R.drawable.ic_alert)); + builder.setBurnInProtectionIcon(Icon.createWithResource(this, R.drawable.ic_alert_burnin)); + } + + if (dataType == ComplicationData.TYPE_RANGED_VALUE) { + builder.setMinValue(0); + builder.setMaxValue(100); + builder.setValue(0); + } + + switch (dataType) { + case ComplicationData.TYPE_ICON: + case ComplicationData.TYPE_SHORT_TEXT: + case ComplicationData.TYPE_RANGED_VALUE: + if (since > 0) { + builder.setShortText(ComplicationText.plainText(DisplayFormat.shortTimeSince(since) + " old")); + } else { + builder.setShortText(ComplicationText.plainText("!old!")); + } + break; + case ComplicationData.TYPE_LONG_TEXT: + builder.setLongTitle(ComplicationText.plainText(aaps.gs(R.string.label_warning_old))); + if (since > 0) { + builder.setLongText(ComplicationText.plainText(String.format(aaps.gs(R.string.label_warning_since), DisplayFormat.shortTimeSince(since)))); + } else { + builder.setLongText(ComplicationText.plainText(aaps.gs(R.string.label_warning_sync_aaps))); + } + break; + case ComplicationData.TYPE_LARGE_IMAGE: + return buildComplicationData(dataType, raw, complicationPendingIntent); + default: + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + break; + } + + builder.setTapAction(exceptionalPendingIntent); + complicationData = builder.build(); + return complicationData; + } + + /** + * If Complication depend on "since" field and need to be updated every minute or not + * and need only update when new DisplayRawData arrive + */ + protected boolean usesSinceField() { + return false; + } + + //============================================================================================== + // COMPLICATION LIFECYCLE + //============================================================================================== + + /* + * Called when a complication has been activated. The method is for any one-time + * (per complication) set-up. + * + * You can continue sending data for the active complicationId until onComplicationDeactivated() + * is called. + */ + @Override + public void onComplicationActivated( + int complicationId, int dataType, ComplicationManager complicationManager) { + Log.d(TAG, "onComplicationActivated(): " + complicationId + " of kind: "+getProviderCanonicalName()); + + Persistence persistence = new Persistence(); + persistence.putString("complication_"+complicationId, getProviderCanonicalName()); + persistence.putBoolean("complication_"+complicationId+"_since", usesSinceField()); + persistence.addToSet(KEY_COMPLICATIONS, "complication_"+complicationId); + + IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); + + messageReceiver = new BaseComplicationProviderService.MessageReceiver(); + localBroadcastManager = LocalBroadcastManager.getInstance(this); + localBroadcastManager.registerReceiver(messageReceiver, messageFilter); + + ListenerService.requestData(this); + checkIfUpdateNeeded(); + } + + /* + * Called when the complication needs updated data from your provider. There are four scenarios + * when this will happen: + * + * 1. An active watch face complication is changed to use this provider + * 2. A complication using this provider becomes active + * 3. The period of time you specified in the manifest has elapsed (UPDATE_PERIOD_SECONDS) + * 4. You triggered an update from your own class via the + * ProviderUpdateRequester.requestUpdate() method. + */ + @Override + public void onComplicationUpdate( + int complicationId, int dataType, ComplicationManager complicationManager) { + Log.d(TAG, "onComplicationUpdate() id: " + complicationId + " of class: "+getProviderCanonicalName()); + + // Create Tap Action so that the user can checkIfUpdateNeeded an update by tapping the complication. + final ComponentName thisProvider = new ComponentName(this, getProviderCanonicalName()); + + // We pass the complication id, so we can only update the specific complication tapped. + final PendingIntent complicationPendingIntent = + ComplicationTapBroadcastReceiver.getTapActionIntent( + aaps.getAppContext(), thisProvider, complicationId, getComplicationAction()); + + final Persistence persistence = new Persistence(); + + final DisplayRawData raw = new DisplayRawData(); + raw.partialUpdateFromPersistence(persistence); + Log.d(TAG, "Complication data: " + raw.toDebugString()); + + // store what is currently rendered since field, to detect it need update + persistence.putString(KEY_LAST_SINCE, DisplayFormat.shortTimeSince(raw.datetime)); + + // by each render we clear stale flag to ensure it is re-rendered at next refresh detection round + persistence.putBoolean(KEY_STALE_REPORTED, false); + + ComplicationData complicationData; + + if (WearUtil.msSince(persistence.whenDataUpdated()) > Constants.STALE_MS) { + // no new data arrived - probably configuration or connection error + final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent( + aaps.getAppContext(), thisProvider, complicationId, ComplicationAction.WARNING_SYNC, persistence.whenDataUpdated()); + complicationData = buildNoSyncComplicationData(dataType, raw, complicationPendingIntent, infoToast, persistence.whenDataUpdated()); + } else if (WearUtil.msSince(raw.datetime) > Constants.STALE_MS) { + // data arriving from phone AAPS, but it is outdated (uploader/NS/xDrip/Sensor error) + final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent( + aaps.getAppContext(), thisProvider, complicationId, ComplicationAction.WARNING_OLD, raw.datetime); + complicationData = buildOutdatedComplicationData(dataType, raw, complicationPendingIntent, infoToast, raw.datetime); + } else { + // data is up-to-date, we can render standard complication + complicationData = buildComplicationData(dataType, raw, complicationPendingIntent); + } + + if (complicationData != null) { + complicationManager.updateComplicationData(complicationId, complicationData); + + } else { + // If no data is sent, we still need to inform the ComplicationManager, so the update + // job can finish and the wake lock isn't held any longer than necessary. + complicationManager.noUpdateRequired(complicationId); + } + } + + /* + * Called when the complication has been deactivated. + */ + @Override + public void onComplicationDeactivated(int complicationId) { + Log.d(TAG, "onComplicationDeactivated(): " + complicationId); + + Persistence persistence = new Persistence(); + persistence.removeFromSet(KEY_COMPLICATIONS, "complication_"+complicationId); + + if (localBroadcastManager != null && messageReceiver != null) { + localBroadcastManager.unregisterReceiver(messageReceiver); + } + Inevitable.kill(TASK_ID_REFRESH_COMPLICATION); + } + + //============================================================================================== + // UPDATE AND REFRESH LOGIC + //============================================================================================== + + /* + * Schedule check for field update + */ + public static void checkIfUpdateNeeded() { + + Persistence p = new Persistence(); + + Log.d(TAG, "Pending check if update needed - "+p.getString(KEY_COMPLICATIONS, "")); + + Inevitable.task(TASK_ID_REFRESH_COMPLICATION, 15 * Constants.SECOND_IN_MS, () -> { + if (WearUtil.rateLimit("complication-checkIfUpdateNeeded", 5)) { + Log.d(TAG, "Checking if update needed"); + requestUpdateIfSinceChanged(); + // We reschedule need for check - to make sure next check will Inevitable go in next 15s + checkIfUpdateNeeded(); + } + }); + + } + + /* + * Check if displayed since field (field that shows how old, in minutes, is reading) + * is up-to-date or need to be changed (a minute or more elapsed) + */ + private static void requestUpdateIfSinceChanged() { + final Persistence persistence = new Persistence(); + + final DisplayRawData raw = new DisplayRawData(); + raw.partialUpdateFromPersistence(persistence); + + final String lastSince = persistence.getString(KEY_LAST_SINCE, "-"); + final String calcSince = DisplayFormat.shortTimeSince(raw.datetime); + final boolean isStale = (WearUtil.msSince(persistence.whenDataUpdated()) > Constants.STALE_MS) + ||(WearUtil.msSince(raw.datetime) > Constants.STALE_MS); + + final boolean staleWasRefreshed = persistence.getBoolean(KEY_STALE_REPORTED, false); + final boolean sinceWasChanged = !lastSince.equals(calcSince); + + if (sinceWasChanged|| (isStale && !staleWasRefreshed)) { + persistence.putString(KEY_LAST_SINCE, calcSince); + persistence.putBoolean(KEY_STALE_REPORTED, isStale); + + Log.d(TAG, "Detected refresh of time needed! Reason: " + + (isStale ? "- stale detected": "") + + (sinceWasChanged ? "- since changed from: "+lastSince+" to: "+calcSince : "")); + + if (isStale) { + // all complications should update to show offline/old warning + requestUpdate(getActiveProviderClasses()); + } else { + // ... but only some require update due to 'since' field change + requestUpdate(getSinceDependingProviderClasses()); + } + } + } + + /* + * Request update for specified list of providers + */ + private static void requestUpdate(Set providers) { + for (String provider: providers) { + Log.d(TAG, "Pending update of "+provider); + // We wait with updating allowing all request, from various sources, to arrive + Inevitable.task("update-req-"+provider, 700, () -> { + if (WearUtil.rateLimit("update-req-"+provider, 2)) { + Log.d(TAG, "Requesting update of "+provider); + final ComponentName componentName = new ComponentName(aaps.getAppContext(), provider); + final ProviderUpdateRequester providerUpdateRequester = new ProviderUpdateRequester(aaps.getAppContext(), componentName); + providerUpdateRequester.requestUpdateAll(); + } + }); + } + } + + /* + * List all Complication providing classes that have active (registered) providers + */ + private static Set getActiveProviderClasses() { + Persistence persistence = new Persistence(); + Set providers = new HashSet<>(); + Set complications = persistence.getSetOf(KEY_COMPLICATIONS); + for (String complication: complications) { + final String providerClass = persistence.getString(complication, ""); + if (providerClass.length() > 0) { + providers.add(providerClass); + } + } + return providers; + } + + /* + * List all Complication providing classes that have active (registered) providers + * and additionally they depend on "since" field + * == they need to be updated not only on data broadcasts, but every minute or so + */ + private static Set getSinceDependingProviderClasses() { + Persistence persistence = new Persistence(); + Set providers = new HashSet<>(); + Set complications = persistence.getSetOf(KEY_COMPLICATIONS); + for (String complication: complications) { + final String providerClass = persistence.getString(complication, ""); + final boolean dependOnSince = persistence.getBoolean(complication+"_since", false); + if ((providerClass.length() > 0)&&(dependOnSince)) { + providers.add(providerClass); + } + } + return providers; + } + + /* + * Listen to broadcast --> new data was stored by ListenerService to Persistence + */ + public class MessageReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Set complications = Persistence.setOf(KEY_COMPLICATIONS); + if (complications.size() > 0) { + checkIfUpdateNeeded(); + // We request all active providers + requestUpdate(getActiveProviderClasses()); + } + } + } + + +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java new file mode 100644 index 0000000000..e9f05c5f79 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java @@ -0,0 +1,49 @@ +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 info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; +import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; + +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_COB_FIELD; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_IOB_FIELD; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class BrCobIobComplication extends BaseComplicationProviderService { + + private static final String TAG = BrCobIobComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + final String cob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(MIN_COB_FIELD); + final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_IOB_FIELD, (MAX_SHORT_FIELD-1) - cob.length())); + + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(DisplayFormat.basalRateSymbol()+raw.sBasalRate)) + .setShortTitle(ComplicationText.plainText(cob + " " + iob)) + .setTapAction(complicationPendingIntent); + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return BrCobIobComplication.class.getCanonicalName(); + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java new file mode 100644 index 0000000000..48341b53ef --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java @@ -0,0 +1,52 @@ +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 android.util.Pair; + +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class CobDetailedComplication extends BaseComplicationProviderService { + + private static final String TAG = CobDetailedComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + + Pair cob = DisplayFormat.detailedCob(raw); + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(cob.first)) + .setTapAction(complicationPendingIntent); + + if (cob.second.length() > 0) { + builder.setShortTitle(ComplicationText.plainText(cob.second)); + } + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return CobDetailedComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.WIZARD; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java new file mode 100644 index 0000000000..40824fd313 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java @@ -0,0 +1,51 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.complications.ComplicationText; +import android.util.Log; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DisplayRawData; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class CobIconComplication extends BaseComplicationProviderService { + + private static final String TAG = CobIconComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(raw.sCOB2)) + .setIcon( + Icon.createWithResource( + this, R.drawable.ic_carbs)) + .setBurnInProtectionIcon( + Icon.createWithResource( + this, R.drawable.ic_carbs)) + .setTapAction(complicationPendingIntent); + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return CobIconComplication.class.getCanonicalName(); + } + + public ComplicationAction getComplicationAction() { + return ComplicationAction.WIZARD; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java new file mode 100644 index 0000000000..1f7dff7ceb --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java @@ -0,0 +1,46 @@ +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 info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; + +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class CobIobComplication extends BaseComplicationProviderService { + + private static final String TAG = CobIobComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + 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(MAX_SHORT_FIELD); + + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(cob)) + .setShortTitle(ComplicationText.plainText(iob)) + .setTapAction(complicationPendingIntent); + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return CobIobComplication.class.getCanonicalName(); + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationAction.java b/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationAction.java new file mode 100644 index 0000000000..9ca1e9c456 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationAction.java @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.complications; + +public enum ComplicationAction { + NONE, + MENU, + WIZARD, + BOLUS, + ECARB, + STATUS, + WARNING_SYNC, + WARNING_OLD +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationTapBroadcastReceiver.java b/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationTapBroadcastReceiver.java new file mode 100644 index 0000000000..61dc8d4dde --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/ComplicationTapBroadcastReceiver.java @@ -0,0 +1,167 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.wearable.complications.ProviderUpdateRequester; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.StringRes; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.interaction.actions.BolusActivity; +import info.nightscout.androidaps.interaction.actions.ECarbActivity; +import info.nightscout.androidaps.interaction.actions.WizardActivity; +import info.nightscout.androidaps.interaction.menus.MainMenuActivity; +import info.nightscout.androidaps.interaction.menus.StatusMenuActivity; +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; +import info.nightscout.androidaps.interaction.utils.WearUtil; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class ComplicationTapBroadcastReceiver extends BroadcastReceiver { + + private static final String TAG = ComplicationTapBroadcastReceiver.class.getSimpleName(); + + private static final String EXTRA_PROVIDER_COMPONENT = + "info.nightscout.androidaps.complications.action.PROVIDER_COMPONENT"; + private static final String EXTRA_COMPLICATION_ID = + "info.nightscout.androidaps.complications.action.COMPLICATION_ID"; + private static final String EXTRA_COMPLICATION_ACTION = + "info.nightscout.androidaps.complications.action.COMPLICATION_ACTION"; + private static final String EXTRA_COMPLICATION_SINCE = + "info.nightscout.androidaps.complications.action.COMPLICATION_SINCE"; + + @Override + public void onReceive(Context context, Intent intent) { + Bundle extras = intent.getExtras(); + ComponentName provider = extras.getParcelable(EXTRA_PROVIDER_COMPONENT); + int complicationId = extras.getInt(EXTRA_COMPLICATION_ID); + String complicationAction = extras.getString(EXTRA_COMPLICATION_ACTION, ComplicationAction.MENU.toString()); + + ComplicationAction action = ComplicationAction.MENU; + try { + action = ComplicationAction.valueOf(ComplicationAction.class, complicationAction); + } catch (IllegalArgumentException | NullPointerException ex) { + // but how? + Log.e(TAG, "Cannot interpret complication action: "+complicationAction); + } + + action = remapActionWithUserPreferences(action); + + // Request an update for the complication that has just been tapped. + ProviderUpdateRequester requester = new ProviderUpdateRequester(context, provider); + requester.requestUpdate(complicationId); + + Intent intentOpen = null; + + switch (action) { + case NONE: + // do nothing + return; + case WIZARD: + intentOpen = new Intent(aaps.getAppContext(), WizardActivity.class); + break; + case BOLUS: + intentOpen = new Intent(aaps.getAppContext(), BolusActivity.class); + break; + case ECARB: + intentOpen = new Intent(aaps.getAppContext(), ECarbActivity.class); + break; + case STATUS: + intentOpen = new Intent(aaps.getAppContext(), StatusMenuActivity.class); + break; + case WARNING_OLD: + case WARNING_SYNC: + long oneAndHalfMinuteAgo = WearUtil.timestamp() - (Constants.MINUTE_IN_MS+Constants.SECOND_IN_MS * 30); + long since = extras.getLong(EXTRA_COMPLICATION_SINCE, oneAndHalfMinuteAgo); + @StringRes int labelId = (action == ComplicationAction.WARNING_SYNC) ? + R.string.msg_warning_sync : R.string.msg_warning_old; + String msg = String.format(aaps.gs(labelId), DisplayFormat.shortTimeSince(since)); + Toast.makeText(aaps.getAppContext(), msg, Toast.LENGTH_LONG).show(); + break; + case MENU: + default: + intentOpen = new Intent(aaps.getAppContext(), MainMenuActivity.class); + } + + if (intentOpen != null) { + // Perform intent - open dialog + intentOpen.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + aaps.getAppContext().startActivity(intentOpen); + } + } + + private ComplicationAction remapActionWithUserPreferences(ComplicationAction originalAction) { + final String userPrefAction = aaps.getComplicationTapAction(); + switch (originalAction) { + case WARNING_OLD: + case WARNING_SYNC: + // warnings cannot be reconfigured by user + return originalAction; + default: + switch (userPrefAction) { + case "menu": + return ComplicationAction.MENU; + case "wizard": + return ComplicationAction.WIZARD; + case "bolus": + return ComplicationAction.BOLUS; + case "ecarb": + return ComplicationAction.ECARB; + case "status": + return ComplicationAction.STATUS; + case "none": + return ComplicationAction.NONE; + case "default": + default: + return originalAction; + } + } + } + + /** + * Returns a pending intent, suitable for use as a tap intent, that causes a complication to be + * toggled and updated. + */ + static PendingIntent getTapActionIntent( + Context context, ComponentName provider, int complicationId, ComplicationAction action) { + Intent intent = new Intent(context, ComplicationTapBroadcastReceiver.class); + intent.putExtra(EXTRA_PROVIDER_COMPONENT, provider); + intent.putExtra(EXTRA_COMPLICATION_ID, complicationId); + intent.putExtra(EXTRA_COMPLICATION_ACTION, action.toString()); + + + // Pass complicationId as the requestCode to ensure that different complications get + // different intents. + return PendingIntent.getBroadcast( + context, complicationId, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + /** + * Returns a pending intent, suitable for use as a tap intent, that causes a complication to be + * toggled and updated. + */ + static PendingIntent getTapWarningSinceIntent( + Context context, ComponentName provider, int complicationId, ComplicationAction action, long since) { + Intent intent = new Intent(context, ComplicationTapBroadcastReceiver.class); + intent.putExtra(EXTRA_PROVIDER_COMPONENT, provider); + intent.putExtra(EXTRA_COMPLICATION_ID, complicationId); + intent.putExtra(EXTRA_COMPLICATION_ACTION, action.toString()); + intent.putExtra(EXTRA_COMPLICATION_SINCE, since); + + + // Pass complicationId as the requestCode to ensure that different complications get + // different intents. + return PendingIntent.getBroadcast( + context, complicationId, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java new file mode 100644 index 0000000000..d9ee729874 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java @@ -0,0 +1,52 @@ +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 android.util.Pair; + +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class IobDetailedComplication extends BaseComplicationProviderService { + + private static final String TAG = IobDetailedComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + + Pair iob = DisplayFormat.detailedIob(raw); + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(iob.first)) + .setTapAction(complicationPendingIntent); + + if (iob.second.length() > 0) { + builder.setShortTitle(ComplicationText.plainText(iob.second)); + } + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return IobDetailedComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.BOLUS; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java new file mode 100644 index 0000000000..ba253a048d --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java @@ -0,0 +1,56 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.complications.ComplicationText; +import android.util.Log; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; + +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class IobIconComplication extends BaseComplicationProviderService { + + private static final String TAG = IobIconComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_SHORT_FIELD); + + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(iob)) + .setIcon(Icon.createWithResource( + this, R.drawable.ic_ins)) + .setBurnInProtectionIcon( + Icon.createWithResource( + this, R.drawable.ic_ins_burnin)) + .setTapAction(complicationPendingIntent); + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return IobIconComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.BOLUS; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java new file mode 100644 index 0000000000..834f40b0a7 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java @@ -0,0 +1,52 @@ +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 info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class LongStatusComplication extends BaseComplicationProviderService { + + private static final String TAG = LongStatusComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + switch (dataType) { + case ComplicationData.TYPE_LONG_TEXT: + + final String glucoseLine = DisplayFormat.longGlucoseLine(raw); + final String detailsLine = DisplayFormat.longDetailsLine(raw); + + final ComplicationData.Builder builderLong = new ComplicationData.Builder(ComplicationData.TYPE_LONG_TEXT) + .setLongTitle(ComplicationText.plainText(glucoseLine)) + .setLongText(ComplicationText.plainText(detailsLine)) + .setTapAction(complicationPendingIntent); + complicationData = builderLong.build(); + + break; + default: + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return LongStatusComplication.class.getCanonicalName(); + } + + @Override + protected boolean usesSinceField() { + return true; + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java new file mode 100644 index 0000000000..da3384d358 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java @@ -0,0 +1,53 @@ +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 info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; +import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class LongStatusFlippedComplication extends BaseComplicationProviderService { + + private static final String TAG = LongStatusFlippedComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + switch (dataType) { + case ComplicationData.TYPE_LONG_TEXT: + + final String glucoseLine = DisplayFormat.longGlucoseLine(raw); + final String detailsLine = DisplayFormat.longDetailsLine(raw); + + final ComplicationData.Builder builderLong = new ComplicationData.Builder(ComplicationData.TYPE_LONG_TEXT) + .setLongTitle(ComplicationText.plainText(detailsLine)) + .setLongText(ComplicationText.plainText(glucoseLine)) + .setTapAction(complicationPendingIntent); + complicationData = builderLong.build(); + + break; + default: + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return LongStatusFlippedComplication.class.getCanonicalName(); + } + + @Override + protected boolean usesSinceField() { + return true; + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java new file mode 100644 index 0000000000..b6f9e94946 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java @@ -0,0 +1,48 @@ +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 info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.DisplayFormat; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class SgvComplication extends BaseComplicationProviderService { + + private static final String TAG = SgvComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + 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)) + .setShortTitle(ComplicationText.plainText(DisplayFormat.shortTrend(raw))) + .setTapAction(complicationPendingIntent); + + complicationData = builder.build(); + break; + default: + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return SgvComplication.class.getCanonicalName(); + } + + @Override + protected boolean usesSinceField() { + return true; + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java new file mode 100644 index 0000000000..4b268ef9c7 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java @@ -0,0 +1,114 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.complications.ComplicationText; +import android.util.Log; + +import androidx.annotation.DrawableRes; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DisplayRawData; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class UploaderBattery extends BaseComplicationProviderService { + + private static final String TAG = UploaderBattery.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + @DrawableRes int batteryIcon = R.drawable.ic_battery_unknown; + @DrawableRes int burnInBatteryIcon = R.drawable.ic_battery_unknown_burnin; + int level = 0; + String levelStr = "???"; + + if (raw.sUploaderBattery.matches("^[0-9]+$")) { + try { + level = Integer.parseInt(raw.sUploaderBattery); + level = Math.max(Math.min(level, 100), 0); + levelStr = level + "%"; + int iconNo = (int)Math.floor(level / 10.0); + if (level > 95) { + iconNo = 10; + } + switch (iconNo) { + case 10: batteryIcon = R.drawable.ic_battery_charging_wireless; break; + case 9: batteryIcon = R.drawable.ic_battery_charging_wireless_90; break; + case 8: batteryIcon = R.drawable.ic_battery_charging_wireless_80; break; + case 7: batteryIcon = R.drawable.ic_battery_charging_wireless_70; break; + case 6: batteryIcon = R.drawable.ic_battery_charging_wireless_60; break; + case 5: batteryIcon = R.drawable.ic_battery_charging_wireless_50; break; + case 4: batteryIcon = R.drawable.ic_battery_charging_wireless_40; break; + case 3: batteryIcon = R.drawable.ic_battery_charging_wireless_30; break; + case 2: batteryIcon = R.drawable.ic_battery_charging_wireless_20; break; + case 1: batteryIcon = R.drawable.ic_battery_charging_wireless_10; break; + case 0: batteryIcon = R.drawable.ic_battery_alert_variant_outline; break; + default: batteryIcon = R.drawable.ic_battery_charging_wireless_outline; + } + + switch (iconNo) { + case 10: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_burnin; break; + case 9: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_90_burnin; break; + case 8: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_80_burnin; break; + case 7: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_70_burnin; break; + case 6: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_60_burnin; break; + case 5: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_50_burnin; break; + case 4: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_40_burnin; break; + case 3: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_30_burnin; break; + case 2: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_20_burnin; break; + case 1: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_10_burnin; break; + case 0: burnInBatteryIcon = R.drawable.ic_battery_alert_variant_outline; break; + default: burnInBatteryIcon = R.drawable.ic_battery_charging_wireless_outline; + } + + + } catch (NumberFormatException ex){ + Log.e(TAG, "Cannot parse battery level of: " + raw.sUploaderBattery); + } + } + + if (dataType == ComplicationData.TYPE_RANGED_VALUE) { + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_RANGED_VALUE) + .setMinValue(0) + .setMaxValue(100) + .setValue(level) + .setShortText(ComplicationText.plainText(levelStr)) + .setIcon(Icon.createWithResource(this, batteryIcon)) + .setBurnInProtectionIcon(Icon.createWithResource(this, burnInBatteryIcon)) + .setTapAction(complicationPendingIntent); + complicationData = builder.build(); + } else if (dataType == ComplicationData.TYPE_SHORT_TEXT) { + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) + .setShortText(ComplicationText.plainText(levelStr)) + .setIcon(Icon.createWithResource(this, batteryIcon)) + .setBurnInProtectionIcon(Icon.createWithResource(this, burnInBatteryIcon)) + .setTapAction(complicationPendingIntent); + complicationData = builder.build(); + } else if (dataType == ComplicationData.TYPE_ICON) { + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_ICON) + .setIcon(Icon.createWithResource(this, batteryIcon)) + .setBurnInProtectionIcon(Icon.createWithResource(this, burnInBatteryIcon)) + .setTapAction(complicationPendingIntent); + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } + + @Override + public String getProviderCanonicalName() { + return UploaderBattery.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.STATUS; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java new file mode 100644 index 0000000000..2448bebee0 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java @@ -0,0 +1,62 @@ +package info.nightscout.androidaps.complications; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Icon; +import android.support.wearable.complications.ComplicationData; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.WindowManager; + +import java.io.IOException; +import java.io.InputStream; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.data.DisplayRawData; + +/* + * Created by dlvoy on 2019-11-12 + */ +public abstract class WallpaperComplication extends BaseComplicationProviderService { + + public abstract String getWallpaperAssetsFileName(); + + private static final String TAG = WallpaperComplication.class.getSimpleName(); + + public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + + ComplicationData complicationData = null; + + if (dataType == ComplicationData.TYPE_LARGE_IMAGE) { + + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager windowManager = (WindowManager) aaps.getAppContext() + .getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getMetrics(metrics); + int width = metrics.widthPixels; + int height = metrics.heightPixels; + + final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_LARGE_IMAGE); + + AssetManager assetManager = getAssets(); + try (InputStream istr = assetManager.open(getWallpaperAssetsFileName())) { + Bitmap bitmap = BitmapFactory.decodeStream(istr); + Bitmap scaled = Bitmap.createScaledBitmap(bitmap, width, height, true); + builder.setLargeImage(Icon.createWithBitmap(scaled)); + } catch (IOException e) { + Log.e(TAG, "Cannot read wallpaper asset: "+e.getMessage(), e); + e.printStackTrace(); + } + + complicationData = builder.build(); + } else { + if (Log.isLoggable(TAG, Log.WARN)) { + Log.w(TAG, "Unexpected complication type " + dataType); + } + } + return complicationData; + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperDarkComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperDarkComplication.java new file mode 100644 index 0000000000..8c84e1d8c3 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperDarkComplication.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.complications; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class WallpaperDarkComplication extends WallpaperComplication { + + @Override + public String getWallpaperAssetsFileName() { + return "watch_dark.jpg"; + } + + @Override + public String getProviderCanonicalName() { + return WallpaperDarkComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.NONE; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperGrayComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperGrayComplication.java new file mode 100644 index 0000000000..bec047f323 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperGrayComplication.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.complications; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class WallpaperGrayComplication extends WallpaperComplication { + + @Override + public String getWallpaperAssetsFileName() { + return "watch_gray.jpg"; + } + + @Override + public String getProviderCanonicalName() { + return WallpaperGrayComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.NONE; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperLightComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperLightComplication.java new file mode 100644 index 0000000000..2d2bbf6f14 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperLightComplication.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.complications; + +/* + * Created by dlvoy on 2019-11-12 + */ +public class WallpaperLightComplication extends WallpaperComplication { + + @Override + public String getWallpaperAssetsFileName() { + return "watch_light.jpg"; + } + + @Override + public String getProviderCanonicalName() { + return WallpaperLightComplication.class.getCanonicalName(); + } + + @Override + public ComplicationAction getComplicationAction() { + return ComplicationAction.NONE; + }; +} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java b/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java new file mode 100644 index 0000000000..e9cf997db8 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java @@ -0,0 +1,269 @@ +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 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 DisplayRawData { + + 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 partialUpdateFromPersistence(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 = DataMap.fromBundle(bundle); + updateData(dataMap); + return dataMap; + } + return null; + } + + private void updateData(DataMap dataMap) { + 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"); + } + + public DataMap updateStatusFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { + Bundle bundle = intent.getBundleExtra("status"); + if (bundle != null) { + DataMap dataMap = DataMap.fromBundle(bundle); + updateStatus(dataMap); + return dataMap; + } + return null; + } + + private void updateStatus(DataMap dataMap) { + 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"); + } + + public DataMap updateBasalsFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { + Bundle bundle = intent.getBundleExtra("basals"); + if (bundle != null) { + DataMap dataMap = DataMap.fromBundle(bundle); + updateBasals(dataMap); + return dataMap; + } + return null; + } + + private void updateBasals(DataMap dataMap) { + WearUtil.getWakeLock("readingPrefs", 500); + loadBasalsAndTemps(dataMap); + } + + 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)); + } + + for (int i = 0; i < bgDataList.size(); i++) { + if (bgDataList.get(i).timestamp < (System.currentTimeMillis() - (Constants.HOUR_IN_MS * 5))) { + bgDataList.remove(i); //Get rid of anything more than 5 hours old + break; + } + } + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java index 83c9ad58b3..7bd43a79e1 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java @@ -40,6 +40,7 @@ import info.nightscout.androidaps.interaction.AAPSPreferences; import info.nightscout.androidaps.R; 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.SafeParse; import info.nightscout.androidaps.interaction.utils.WearUtil; @@ -512,12 +513,14 @@ public class ListenerService extends WearableListenerService implements GoogleAp Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("status", dataMap.toBundle()); + Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } else if (path.equals(BASAL_DATA_PATH)){ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("basals", dataMap.toBundle()); + Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } else if (path.equals(NEW_PREFERENCES_PATH)){ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); @@ -541,6 +544,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("data", dataMap.toBundle()); + Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Constants.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Constants.java new file mode 100644 index 0000000000..2af3ef34b6 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Constants.java @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.interaction.utils; + +public class Constants { + + public static final long SECOND_IN_MS = 1000; + public static final long MINUTE_IN_MS = 60000; + public static final long HOUR_IN_MS = 3600000; + public static final long DAY_IN_MS = 86400000; + public static final long WEEK_IN_MS = DAY_IN_MS * 7; + public static final long MONTH_IN_MS = DAY_IN_MS * 30; + + public static final long STALE_MS = Constants.MINUTE_IN_MS * 12; + +} 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 new file mode 100644 index 0000000000..366ca28eed --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java @@ -0,0 +1,129 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.util.Pair; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.data.DisplayRawData; + +public class DisplayFormat { + + /** + * Maximal lengths of fields/labels shown in complications + */ + public static final int MAX_LONG_FIELD = 22; // this is empirical, above that many watch faces start to ellipsize + public static final int MAX_SHORT_FIELD = 7; // according to Wear OS docs for TYPE_SHORT_TEXT + public static final int MIN_COB_FIELD = 3; // since carbs are 0..99g + public static final int MIN_IOB_FIELD = 3; // IoB can range from like .1U to 99U + + public static String deltaSymbol() { + return aaps.areComplicationsUnicode() ? "\u0394" : ""; + } + + public static String verticalSeparatorSymbol() { + return aaps.areComplicationsUnicode() ? "\u205E" : "|"; + } + + public static String basalRateSymbol() { + return aaps.areComplicationsUnicode() ? "\u238D\u2006" : ""; + } + + public static String shortTimeSince(final long refTime) { + + long deltaTimeMs = WearUtil.msSince(refTime); + + if (deltaTimeMs < Constants.MINUTE_IN_MS) { + return "0'"; + } else if (deltaTimeMs < Constants.HOUR_IN_MS) { + int minutes = (int) (deltaTimeMs / Constants.MINUTE_IN_MS); + return minutes + "'"; + } else if (deltaTimeMs < Constants.DAY_IN_MS) { + int hours = (int) (deltaTimeMs / Constants.HOUR_IN_MS); + return hours + "h"; + } else { + int days = (int) (deltaTimeMs / Constants.DAY_IN_MS); + if (days < 7) { + return days + "d"; + } else { + int weeks = days / 7; + return weeks + "d"; + } + } + } + + public static String shortTrend(final DisplayRawData raw) { + String minutes = shortTimeSince(raw.datetime); + String delta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_SHORT_FIELD-1); + + if (minutes.length() + delta.length() + 1 < MAX_SHORT_FIELD) { + delta = deltaSymbol() + delta; + } + + return minutes + " " + delta; + } + + public static String longGlucoseLine(final DisplayRawData raw) { + return raw.sSgv + raw.sDirection + " " + deltaSymbol() + (new SmallestDoubleString(raw.sDelta)).minimise(8) + " (" + shortTimeSince(raw.datetime) + ")"; + } + + public static String longDetailsLine(final DisplayRawData raw) { + + final String SEP_LONG = " " + verticalSeparatorSymbol() + " "; + final String SEP_SHORT = " " + verticalSeparatorSymbol() + " "; + 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; + if (line.length() <= MAX_LONG_FIELD) { + return line; + } + line = raw.sCOB2 + SEP_SHORT + raw.sIOB1 + SEP_SHORT + raw.sBasalRate; + if (line.length() <= MAX_LONG_FIELD) { + return line; + } + + int remainingMax = MAX_LONG_FIELD - (raw.sCOB2.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); + final String smallestIoB = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_IOB_FIELD, remainingMax)); + line = raw.sCOB2 + SEP_SHORT + smallestIoB + SEP_SHORT + raw.sBasalRate; + if (line.length() <= MAX_LONG_FIELD) { + return line; + } + + remainingMax = MAX_LONG_FIELD - (smallestIoB.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); + final String simplifiedCob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_COB_FIELD, remainingMax)); + + line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.sBasalRate; + if (line.length() <= MAX_LONG_FIELD) { + return line; + } + + line = simplifiedCob + SEP_MIN + smallestIoB + SEP_MIN + raw.sBasalRate; + + return line; + } + + public static Pair detailedIob(DisplayRawData raw) { + final String iob1 = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_SHORT_FIELD); + String iob2 = ""; + if (raw.sIOB2.contains("|")) { + + String[] iobs = raw.sIOB2.replace("(", "").replace(")", "").split("\\|"); + if (iobs.length == 2) { + final String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_IOB_FIELD); + final String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_SHORT_FIELD-1) - Math.max(MIN_IOB_FIELD, iobBolus.length())); + iob2 = iobBolus+" "+iobBasal; + } + } + return Pair.create(iob1, iob2); + } + + public static Pair detailedCob(final DisplayRawData raw) { + SmallestDoubleString cobMini = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE); + + String cob2 = ""; + if (cobMini.getExtra().length() > 0) { + cob2 = cobMini.getExtra() + cobMini.getUnits(); + } + final String cob1 = cobMini.minimise(MAX_SHORT_FIELD); + return Pair.create(cob1, cob2); + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java new file mode 100644 index 0000000000..21944da9c7 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java @@ -0,0 +1,117 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.os.PowerManager; +import android.util.Log; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created for xDrip by jamorham on 07/03/2018 + * Adapted for AAPS by dlvoy on 2019-11-11 + * + * Tasks which are fired from events can be scheduled here and only execute when they become idle + * and are not being rescheduled within their wait window. + * + */ + +public class Inevitable { + + private static final String TAG = Inevitable.class.getSimpleName(); + private static final int MAX_QUEUE_TIME = (int) Constants.MINUTE_IN_MS * 6; + private static final boolean d = true; + + private static final ConcurrentHashMap tasks = new ConcurrentHashMap<>(); + + public static synchronized void task(final String id, long idle_for, Runnable runnable) { + if (idle_for > MAX_QUEUE_TIME) { + throw new RuntimeException(id + " Requested time: " + idle_for + " beyond max queue time"); + } + final Task task = tasks.get(id); + if (task != null) { + // if it already exists then extend the time + task.extendTime(idle_for); + + if (d) + Log.d(TAG, "Extending time for: " + id + " to " + WearUtil.dateTimeText(task.when)); + } else { + // otherwise create new task + if (runnable == null) return; // extension only if already exists + tasks.put(id, new Task(id, idle_for, runnable)); + + if (d) { + Log.d(TAG, "Creating task: " + id + " due: " + WearUtil.dateTimeText(tasks.get(id).when)); + } + + // create a thread to wait and execute in background + final Thread t = new Thread(() -> { + final PowerManager.WakeLock wl = WearUtil.getWakeLock(id, MAX_QUEUE_TIME + 5000); + try { + boolean running = true; + // wait for task to be due or killed + while (running) { + WearUtil.threadSleep(500); + final Task thisTask = tasks.get(id); + running = thisTask != null && !thisTask.poll(); + } + } finally { + WearUtil.releaseWakeLock(wl); + } + }); + t.setPriority(Thread.MIN_PRIORITY); + //t.setDaemon(true); + t.start(); + } + } + + public static synchronized void stackableTask(String id, long idle_for, Runnable runnable) { + int stack = 0; + while (tasks.get(id = id + "-" + stack) != null) { + stack++; + } + if (stack > 0) { + Log.d(TAG, "Task stacked to: " + id); + } + task(id, idle_for, runnable); + } + + public static void kill(final String id) { + tasks.remove(id); + } + + public static boolean waiting(final String id) { + return tasks.containsKey(id); + } + + private static class Task { + private long when; + private final Runnable what; + private final String id; + + Task(String id, long offset, Runnable what) { + this.what = what; + this.id = id; + extendTime(offset); + } + + public void extendTime(long offset) { + this.when = WearUtil.timestamp() + offset; + } + + public boolean poll() { + final long till = WearUtil.msTill(when); + if (till < 1) { + if (d) Log.d(TAG, "Executing task! " + this.id); + tasks.remove(this.id); // early remove to allow overlapping scheduling + what.run(); + return true; + } else if (till > MAX_QUEUE_TIME) { + Log.wtf(TAG, "Task: " + this.id + " In queue too long: " + till); + tasks.remove(this.id); + return true; + } + return 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 new file mode 100644 index 0000000000..805aa5aafa --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java @@ -0,0 +1,94 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.content.SharedPreferences; +import android.util.Base64; + +import com.google.android.gms.wearable.DataMap; + +import java.util.Set; + +import info.nightscout.androidaps.aaps; + +/** + * Created by dlvoy on 2019-11-12 + */ +public class Persistence { + + final SharedPreferences preferences; + public static final String COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY = + "info.nightscout.androidaps.complications.COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY"; + + public Persistence() { + preferences = aaps.getAppContext().getSharedPreferences(COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY, 0); + } + + public DataMap getDataMap(String key) { + if (preferences.contains("raw_data")) { + final String rawB64Data = preferences.getString(key, null); + byte[] rawData = Base64.decode(rawB64Data, Base64.DEFAULT); + try { + DataMap dataMap = DataMap.fromByteArray(rawData); + return dataMap; + } catch (IllegalArgumentException ex) { + + } + } + return null; + } + + public void putDataMap(String key, DataMap dataMap) { + preferences.edit().putString(key, Base64.encodeToString(dataMap.toByteArray(), Base64.DEFAULT)).apply(); + } + + public String getString(String key, String defaultValue) { + return preferences.getString(key, defaultValue); + } + + public void putString(String key, String value) { + preferences.edit().putString(key, value).apply(); + } + + public boolean getBoolean(String key, boolean defaultValue) { + return preferences.getBoolean(key, defaultValue); + } + + public void putBoolean(String key, boolean value) { + preferences.edit().putBoolean(key, value).apply(); + } + + public long whenDataUpdated() { + return preferences.getLong("data_updated_at", 0); + } + + private void markDataUpdated() { + preferences.edit().putLong("data_updated_at", WearUtil.timestamp()).apply(); + } + + public Set getSetOf(String key) { + return WearUtil.explodeSet(getString(key, ""), "|"); + } + + public void addToSet(String key, String value) { + final Set set = WearUtil.explodeSet(getString(key, ""), "|"); + set.add(value); + putString(key, WearUtil.joinSet(set, "|")); + } + + public void removeFromSet(String key, String value) { + final Set set = WearUtil.explodeSet(getString(key, ""), "|"); + set.remove(value); + putString(key, WearUtil.joinSet(set, "|")); + } + + public static void storeDataMap(String key, DataMap dataMap) { + Persistence p = new Persistence(); + p.putDataMap(key, dataMap); + p.markDataUpdated(); + } + + public static Set setOf(String key) { + Persistence p = new Persistence(); + return p.getSetOf(key); + } + +} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java new file mode 100644 index 0000000000..5202daf8e4 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java @@ -0,0 +1,130 @@ +package info.nightscout.androidaps.interaction.utils; + +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Helper to minimise various floating point values, with or without unit, to fit into specified + * and limited size, scarifying precision (rounding up) and extra characters like leading zero, + * following zero(s) in fractional part, extra plus sign etc. + * + * Created by dlvoy on 2019-11-12 + */ +public class SmallestDoubleString { + + private String sign = ""; + private String decimal = ""; + private String separator = ""; + private String fractional = ""; + private String extra = ""; + private String units = ""; + + private final Units withUnits; + + public enum Units { + SKIP, + USE + } + + private static Pattern pattern = Pattern.compile("^([+-]?)([0-9]*)([,.]?)([0-9]*)(\\([^)]*\\))?(.*?)$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE ); + + public SmallestDoubleString(String inputString) { + this(inputString, Units.SKIP); + } + + public SmallestDoubleString(String inputString, Units withUnits) { + Matcher matcher = pattern.matcher(inputString); + matcher.matches(); + + sign = matcher.group(1); + decimal = matcher.group(2); + separator = matcher.group(3); + fractional = matcher.group(4); + units = matcher.group(6); + + if (fractional == null || fractional.length() == 0) { + separator = ""; + fractional = ""; + } + if (decimal == null || decimal.length() == 0) { + decimal = ""; + } + if (separator == null || separator.length() == 0) { + separator = ""; + } + if (sign == null || sign.length() == 0) { + sign = ""; + } + + final String extraCandidate = matcher.group(5); + if (extraCandidate != null && extraCandidate.length() > 2) { + extra = extraCandidate.substring(1, extraCandidate.length()-1); + } + + if (units != null) { + units = units.trim(); + } + + this.withUnits = withUnits; + } + + public String minimise(int maxSize) { + if (Integer.parseInt("0"+fractional) == 0) { + separator = ""; + fractional = ""; + } + if (Integer.parseInt("0"+decimal) == 0 && (fractional.length() >0)) { + decimal = ""; + } + if (currentLen() <= maxSize) + return toString(); + + if (sign.equals("+")) { + sign = ""; + } + if (currentLen() <= maxSize) { + return toString(); + } + + while ((fractional.length() > 1)&&(fractional.charAt(fractional.length()-1) == '0')) { + fractional = fractional.substring(0, fractional.length()-1); + } + if (currentLen() <= maxSize) { + return toString(); + } + + if ((fractional.length() > 0)&&(decimal.length() > 0)) { + int remainingForFraction = maxSize-currentLen()+fractional.length(); + String formatCandidate = "#"; + if (remainingForFraction>=1) { + formatCandidate = "#."+("#######".substring(0, remainingForFraction)); + } + DecimalFormat df = new DecimalFormat(formatCandidate); + df.setRoundingMode(RoundingMode.HALF_UP); + + return sign + df.format(Double.parseDouble(decimal+"."+fractional)).replace(".", separator) + + ((withUnits == Units.USE) ? units : ""); + } + return toString(); + } + + private int currentLen() { + return sign.length() + decimal.length() + separator.length() + fractional.length() + + ((withUnits == Units.USE) ? units.length() : 0); + } + + public String toString() { + return sign+decimal+separator+fractional + + ((withUnits == Units.USE) ? units : ""); + } + + public String getExtra() { + return extra; + } + + public String getUnits() { return units; } + + +} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java index ca63749888..2a400b4fa0 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java @@ -1,19 +1,127 @@ package info.nightscout.androidaps.interaction.utils; -import java.time.LocalDateTime; +import android.content.Context; +import android.content.Intent; +import android.os.PowerManager; +import android.util.Log; + import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.data.DisplayRawData; /** * Created by andy on 3/5/19. + * Adapted by dlvoy on 2019-11-06 using code from jamorham JoH class */ public class WearUtil { + private final static boolean debug_wakelocks = false; + private static final Map rateLimits = new HashMap(); + private static final String TAG = WearUtil.class.getName(); + + //============================================================================================== + // Time related util methods + //============================================================================================== public static String dateTimeText(long timeInMs) { Date d = new Date(timeInMs); return "" + d.getDay() + "." + d.getMonth() + "." + d.getYear() + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); } + public static long timestamp() { + return System.currentTimeMillis(); + } + public static long msSince(long when) { + return (timestamp() - when); + } + + public static long msTill(long when) { + return (when - timestamp()); + } + + //============================================================================================== + // Thread and power management utils + //============================================================================================== + + // return true if below rate limit + public static synchronized boolean rateLimit(String name, int seconds) { + // check if over limit + if ((rateLimits.containsKey(name)) && (timestamp() - rateLimits.get(name) < (seconds * 1000))) { + Log.d(TAG, name + " rate limited: " + seconds + " seconds"); + return false; + } + // not over limit + rateLimits.put(name, timestamp()); + return true; + } + + public static PowerManager.WakeLock getWakeLock(final String name, int millis) { + final PowerManager pm = (PowerManager) aaps.getAppContext().getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AAPS::"+name); + wl.acquire(millis); + if (debug_wakelocks) Log.d(TAG, "getWakeLock: " + name + " " + wl.toString()); + return wl; + } + + public static void releaseWakeLock(PowerManager.WakeLock wl) { + if (debug_wakelocks) Log.d(TAG, "releaseWakeLock: " + wl.toString()); + if (wl == null) return; + if (wl.isHeld()) wl.release(); + } + + public static void startActivity(Class c) { + aaps.getAppContext().startActivity(getStartActivityIntent(c)); + } + + + public static Intent getStartActivityIntent(Class c) { + return new Intent(aaps.getAppContext(), c).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + + + public static void threadSleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + // + } + } + + public static String joinSet(Set set, String separator) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (String item : set) { + final String itemToAdd = item.trim(); + if (itemToAdd.length() > 0) { + if (i > 0) { + sb.append(separator); + } + i++; + sb.append(itemToAdd); + } + } + return sb.toString(); + } + + public static Set explodeSet(String joined, String separator) { + // special RegEx literal \\Q starts sequence we escape, \\E ends is + // we use it to escape separator for use in RegEx + String[] items = joined.split("\\Q"+separator+"\\E"); + Set set = new HashSet<>(); + for (String item : items) { + final String itemToAdd = item.trim(); + if (itemToAdd.length() > 0) { + set.add(itemToAdd); + } + } + return set; + } } 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 229eb64f1b..222f1eda6e 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java @@ -10,7 +10,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; -import android.os.Bundle; import android.os.PowerManager; import android.preference.PreferenceManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -33,20 +32,18 @@ import com.ustwo.clockwise.common.WatchFaceTime; import com.ustwo.clockwise.common.WatchShape; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import info.nightscout.androidaps.data.BasalWatchData; -import info.nightscout.androidaps.data.BgWatchData; -import info.nightscout.androidaps.data.BolusWatchData; +import info.nightscout.androidaps.complications.BaseComplicationProviderService; +import info.nightscout.androidaps.data.DisplayRawData; import info.nightscout.androidaps.data.ListenerService; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.TempWatchData; import lecho.lib.hellocharts.view.LineChartView; /** * Created by emmablack on 12/29/14. * Updated by andrew-warrington on 02-Jan-2018. + * Refactored by dlvoy on 2019-11-2019 */ public abstract class BaseWatchFace extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -54,13 +51,10 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen public static final long[] vibratePattern = {0,400,300,400,300,400}; public TextView mTime, mSgv, mDirection, mTimestamp, mUploaderBattery, mRigBattery, mDelta, mAvgDelta, mStatus, mBasalRate, mIOB1, mIOB2, mCOB1, mCOB2, mBgi, mLoop, mDay, mMonth, isAAPSv2, mHighLight, mLowLight; public ImageView mGlucoseDial, mDeltaGauge, mHourHand, mMinuteHand; - public long datetime; public RelativeLayout mRelativeLayout; public LinearLayout mLinearLayout, mLinearLayout2, mDate, mChartTap, mMainMenuTap; - public long sgvLevel = 0; public int ageLevel = 1; public int loopLevel = 1; - public int batteryLevel = 1; public int highColor = Color.YELLOW; public int lowColor = Color.RED; public int midColor = Color.WHITE; @@ -74,11 +68,9 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen public int pointSize = 2; public BgGraphBuilder bgGraphBuilder; public LineChartView chart; - 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 DisplayRawData rawData = new DisplayRawData(); public PowerManager.WakeLock wakeLock; // related endTime manual layout @@ -90,26 +82,9 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen protected SharedPreferences sharedPrefs; - public boolean detailedIOB = false; - public boolean showBGI = false; public boolean forceSquareCanvas = false; //set to true by the Steampunk watch face. - public long openApsStatus; - public String externalStatusString = "no status"; - public String sSgv = "---"; - public String sDirection = "--"; - public String sUploaderBattery = "--"; - public String sRigBattery = "--"; - public String sDelta = "--"; - public String sAvgDelta = "--"; - public String sBasalRate = "-.--U/h"; - public String sIOB1 = "IOB"; - public String sIOB2 = "-.--"; - public String sCOB1 = "Carb"; - public String sCOB2 = "--g"; - public String sBgi = "--"; public String sMinute = "0"; public String sHour = "0"; - public String sUnits = "-"; @Override public void onCreate() { @@ -126,6 +101,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); sharedPrefs.registerOnSharedPreferenceChangeListener(this); + + BaseComplicationProviderService.turnOff(); } @Override @@ -197,11 +174,11 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } public double timeSince() { - return System.currentTimeMillis() - datetime; + return System.currentTimeMillis() - rawData.datetime; } public String readingAge(boolean shortString) { - if (datetime == 0) { return shortString?"--'":"-- Minute ago"; } + if (rawData.datetime == 0) { return shortString?"--'":"-- Minute ago"; } int minutesAgo = (int) Math.floor(timeSince()/(1000*60)); if (minutesAgo == 1) { return minutesAgo + (shortString?"'":" Minute ago"); @@ -266,50 +243,20 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen @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"); - datetime = dataMap.getLong("timestamp"); - sSgv = dataMap.getString("sgvString"); - sDirection = dataMap.getString("slopeArrow"); - sDelta = dataMap.getString("delta"); - sAvgDelta = dataMap.getString("avgDelta"); - sUnits = dataMap.getString("glucoseUnits"); - if (chart != null) { - addToWatchSet(dataMap); + if (layoutSet) { + final DataMap dataMap = rawData.updateDataFromMessage(intent, wakeLock); + if (chart != null && dataMap != null) { + rawData.addToWatchSet(dataMap); setupCharts(); } - } - - bundle = intent.getBundleExtra("status"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(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"); + rawData.updateStatusFromMessage(intent, wakeLock); } setDataFields(); setColor(); - bundle = intent.getBundleExtra("basals"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(500); - loadBasalsAndTemps(dataMap); + if (layoutSet) { + rawData.updateBasalsFromMessage(intent, wakeLock); } mRelativeLayout.measure(specW, specH); @@ -328,7 +275,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mSgv != null) { if (sharedPrefs.getBoolean("showBG", true)) { - mSgv.setText(sSgv); + mSgv.setText(rawData.sSgv); mSgv.setVisibility(View.VISIBLE); } else { //leave the textview there but invisible, as a height holder for the empty space above the white line @@ -341,7 +288,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mDirection != null) { if (sharedPrefs.getBoolean("show_direction", true)) { - mDirection.setText(sDirection); + mDirection.setText(rawData.sDirection); mDirection.setVisibility(View.VISIBLE); } else { mDirection.setVisibility(View.GONE); @@ -350,7 +297,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mDelta != null) { if (sharedPrefs.getBoolean("showDelta", true)) { - mDelta.setText(sDelta); + mDelta.setText(rawData.sDelta); mDelta.setVisibility(View.VISIBLE); } else { mDelta.setVisibility(View.GONE); @@ -359,7 +306,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mAvgDelta != null) { if (sharedPrefs.getBoolean("showAvgDelta", true)) { - mAvgDelta.setText(sAvgDelta); + mAvgDelta.setText(rawData.sAvgDelta); mAvgDelta.setVisibility(View.VISIBLE); } else { mAvgDelta.setVisibility(View.GONE); @@ -367,7 +314,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } if (mCOB1 != null && mCOB2 != null) { - mCOB2.setText(sCOB2); + mCOB2.setText(rawData.sCOB2); if (sharedPrefs.getBoolean("show_cob", true)) { mCOB1.setVisibility(View.VISIBLE); mCOB2.setVisibility(View.VISIBLE); @@ -377,7 +324,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } //deal with cases where there is only the value shown for COB, and not the label } else if (mCOB2 != null) { - mCOB2.setText(sCOB2); + mCOB2.setText(rawData.sCOB2); if (sharedPrefs.getBoolean("show_cob", true)) { mCOB2.setVisibility(View.VISIBLE); } else { @@ -389,12 +336,12 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (sharedPrefs.getBoolean("show_iob", true)) { mIOB1.setVisibility(View.VISIBLE); mIOB2.setVisibility(View.VISIBLE); - if (detailedIOB) { - mIOB1.setText(sIOB1); - mIOB2.setText(sIOB2); + if (rawData.detailedIOB) { + mIOB1.setText(rawData.sIOB1); + mIOB2.setText(rawData.sIOB2); } else { mIOB1.setText("IOB"); - mIOB2.setText(sIOB1); + mIOB2.setText(rawData.sIOB1); } } else { mIOB1.setVisibility(View.GONE); @@ -404,10 +351,10 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } else if (mIOB2 != null) { if (sharedPrefs.getBoolean("show_iob", true)) { mIOB2.setVisibility(View.VISIBLE); - if (detailedIOB) { - mIOB2.setText(sIOB2); + if (rawData.detailedIOB) { + mIOB2.setText(rawData.sIOB2); } else { - mIOB2.setText(sIOB1); + mIOB2.setText(rawData.sIOB1); } } else { mIOB2.setText(""); @@ -434,13 +381,13 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mUploaderBattery != null) { if (sharedPrefs.getBoolean("show_uploader_battery", true)) { if (isAAPSv2 != null) { - mUploaderBattery.setText(sUploaderBattery + "%"); + mUploaderBattery.setText(rawData.sUploaderBattery + "%"); mUploaderBattery.setVisibility(View.VISIBLE); } else { if (sharedPrefs.getBoolean("showExternalStatus", true)) { - mUploaderBattery.setText("U: " + sUploaderBattery + "%"); + mUploaderBattery.setText("U: " + rawData.sUploaderBattery + "%"); } else { - mUploaderBattery.setText("Uploader: " + sUploaderBattery + "%"); + mUploaderBattery.setText("Uploader: " + rawData.sUploaderBattery + "%"); } } } else { @@ -450,7 +397,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mRigBattery != null) { if (sharedPrefs.getBoolean("show_rig_battery", false)) { - mRigBattery.setText(sRigBattery); + mRigBattery.setText(rawData.sRigBattery); mRigBattery.setVisibility(View.VISIBLE); } else { mRigBattery.setVisibility(View.GONE); @@ -459,7 +406,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mBasalRate != null) { if (sharedPrefs.getBoolean("show_temp_basal", true)) { - mBasalRate.setText(sBasalRate); + mBasalRate.setText(rawData.sBasalRate); mBasalRate.setVisibility(View.VISIBLE); } else { mBasalRate.setVisibility(View.GONE); @@ -467,8 +414,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } if (mBgi != null) { - if (showBGI) { - mBgi.setText(sBgi); + if (rawData.showBGI) { + mBgi.setText(rawData.sBgi); mBgi.setVisibility(View.VISIBLE); } else { mBgi.setVisibility(View.GONE); @@ -477,7 +424,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mStatus != null) { if (sharedPrefs.getBoolean("showExternalStatus", true)) { - mStatus.setText(externalStatusString); + mStatus.setText(rawData.externalStatusString); mStatus.setVisibility(View.VISIBLE); } else { mStatus.setVisibility(View.GONE); @@ -487,8 +434,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen if (mLoop != null) { if (sharedPrefs.getBoolean("showExternalStatus", true)) { mLoop.setVisibility(View.VISIBLE); - if (openApsStatus != -1) { - int mins = (int) ((System.currentTimeMillis() - openApsStatus) / 1000 / 60); + if (rawData.openApsStatus != -1) { + int mins = (int) ((System.currentTimeMillis() - rawData.openApsStatus) / 1000 / 60); mLoop.setText(mins + "'"); if (mins > 14) { loopLevel = 0; @@ -594,50 +541,13 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } } - 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)); - } - - 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; - } - } - } - public void setupCharts() { - if(bgDataList.size() > 0) { //Dont crash things just because we dont have values, people dont like crashy things + 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 (lowResMode) { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, predictionList, tempWatchDataList, basalWatchDataList, bolusWatchDataList, pointSize, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), rawData, pointSize, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); } else { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList,predictionList, tempWatchDataList, basalWatchDataList, bolusWatchDataList, pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), rawData, pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); } chart.setLineChartData(bgGraphBuilder.lineData()); @@ -646,54 +556,4 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen } } - 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); - } - } - } } 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 4a401489d9..b40e15cfd4 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java @@ -18,6 +18,7 @@ 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.DisplayRawData; import info.nightscout.androidaps.data.TempWatchData; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.AxisValue; @@ -115,6 +116,42 @@ public class BgGraphBuilder { this.end_time = (predictionEndTime>end_time)?predictionEndTime:end_time; } + public BgGraphBuilder(Context context, DisplayRawData 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, DisplayRawData 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); + } + public LineChartData lineData() { LineChartData lineData = new LineChartData(defaultLines()); lineData.setAxisYLeft(yAxis()); 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 b949e3832a..31d293929a 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java @@ -48,13 +48,13 @@ public class Cockpit extends BaseWatchFace { setTextSizes(); if (mHighLight != null && mLowLight != null) { - if (sgvLevel == 1) { + if (rawData.sgvLevel == 1) { mHighLight.setBackgroundResource(R.drawable.airplane_led_yellow_lit); mLowLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); - } else if (sgvLevel == 0) { + } else if (rawData.sgvLevel == 0) { mHighLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); mLowLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); - } else if (sgvLevel == -1) { + } else if (rawData.sgvLevel == -1) { mHighLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); mLowLight.setBackgroundResource(R.drawable.airplane_led_red_lit); } @@ -82,7 +82,7 @@ public class Cockpit extends BaseWatchFace { protected void setTextSizes() { if (mIOB2 != null) { - if (detailedIOB) { + if (rawData.detailedIOB) { if (bIsRound) { mIOB2.setTextSize(10); } else { 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 430a3feb47..209e6214e7 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java @@ -68,15 +68,15 @@ public class Home extends BaseWatchFace { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView)); mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 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 (sgvLevel == 0) { + } else if (rawData.sgvLevel == 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 (sgvLevel == -1) { + } else if (rawData.sgvLevel == -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)); @@ -88,7 +88,7 @@ public class Home extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery)); } else { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty)); @@ -133,15 +133,15 @@ public class Home extends BaseWatchFace { if (getCurrentWatchMode() == WatchMode.INTERACTIVE) { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_stripe_background)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background)); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 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 (sgvLevel == 0) { + } else if (rawData.sgvLevel == 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 (sgvLevel == -1) { + } else if (rawData.sgvLevel == -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)); @@ -153,7 +153,7 @@ public class Home extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(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 41c9fd3029..fbc69f3c1a 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java @@ -78,13 +78,13 @@ public class Home2 extends BaseWatchFace { setTextSizes(); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (sgvLevel == 0) { + } else if (rawData.sgvLevel == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (sgvLevel == -1) { + } else if (rawData.sgvLevel == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); } @@ -95,7 +95,7 @@ public class Home2 extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery)); } else { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty)); @@ -176,13 +176,13 @@ public class Home2 extends BaseWatchFace { setTextSizes(); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); - } else if (sgvLevel == 0) { + } else if (rawData.sgvLevel == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); - } else if (sgvLevel == -1) { + } else if (rawData.sgvLevel == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); } @@ -193,7 +193,7 @@ public class Home2 extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); } else { mUploaderBattery.setTextColor(Color.RED); @@ -229,7 +229,7 @@ public class Home2 extends BaseWatchFace { if (mIOB1 != null && mIOB2 != null) { - if (detailedIOB) { + if (rawData.detailedIOB) { 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 ffb8dce3ad..75b7d995dd 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java @@ -52,15 +52,15 @@ public class LargeHome extends BaseWatchFace { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mLinearLayout)); mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 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 (sgvLevel == 0) { + } else if (rawData.sgvLevel == 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 (sgvLevel == -1) { + } else if (rawData.sgvLevel == -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)); @@ -72,7 +72,7 @@ public class LargeHome extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery)); } else { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty)); @@ -86,15 +86,15 @@ public class LargeHome extends BaseWatchFace { if (getCurrentWatchMode() == WatchMode.INTERACTIVE) { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_stripe_background)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background)); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 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 (sgvLevel == 0) { + } else if (rawData.sgvLevel == 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 (sgvLevel == -1) { + } else if (rawData.sgvLevel == -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)); @@ -106,7 +106,7 @@ public class LargeHome extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (batteryLevel == 1) { + if (rawData.batteryLevel == 1) { mUploaderBattery.setTextColor(Color.WHITE); } else { mUploaderBattery.setTextColor(Color.RED); @@ -116,15 +116,15 @@ public class LargeHome extends BaseWatchFace { } else { mRelativeLayout.setBackgroundColor(Color.BLACK); mLinearLayout.setBackgroundColor(Color.LTGRAY); - if (sgvLevel == 1) { + if (rawData.sgvLevel == 1) { mSgv.setTextColor(Color.YELLOW); mDirection.setTextColor(Color.YELLOW); mDelta.setTextColor(Color.YELLOW); - } else if (sgvLevel == 0) { + } else if (rawData.sgvLevel == 0) { mSgv.setTextColor(Color.WHITE); mDirection.setTextColor(Color.WHITE); mDelta.setTextColor(Color.WHITE); - } else if (sgvLevel == -1) { + } else if (rawData.sgvLevel == -1) { mSgv.setTextColor(Color.RED); mDirection.setTextColor(Color.RED); mDelta.setTextColor(Color.RED); 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 c42aff1518..a600290a04 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java @@ -83,24 +83,24 @@ public class Steampunk extends BaseWatchFace { } } - if (!sSgv.equals("---")) { + if (!rawData.sSgv.equals("---")) { float rotationAngle = 0f; //by default, show ? on the dial (? is at 0 degrees on the dial) - if (!sUnits.equals("-")) { + if (!rawData.sUnits.equals("-")) { //ensure the glucose dial is the correct units - if (sUnits.equals("mmol")) { + if (rawData.sUnits.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 (sUnits.equals("mmol")) { - rotationAngle = Float.valueOf(sSgv) * 18f; //convert to mg/dL, which is equivalent to degrees + if (rawData.sUnits.equals("mmol")) { + rotationAngle = Float.valueOf(rawData.sSgv) * 18f; //convert to mg/dL, which is equivalent to degrees } else { - rotationAngle = Float.valueOf(sSgv); //if glucose a value is received, use it to determine the amount of rotation of the dial. + rotationAngle = Float.valueOf(rawData.sSgv); //if glucose a value is received, use it to determine the amount of rotation of the dial. } } @@ -122,36 +122,36 @@ public class Steampunk extends BaseWatchFace { //set the delta gauge and rotate the delta pointer float deltaIsNegative = 1f; //by default go clockwise - if (!sAvgDelta.equals("--")) { //if a legitimate delta value is received, then... - if (sAvgDelta.substring(0,1).equals("-")) deltaIsNegative = -1f; //if the delta is negative, go counter-clockwise + if (!rawData.sAvgDelta.equals("--")) { //if a legitimate delta value is received, then... + if (rawData.sAvgDelta.substring(0,1).equals("-")) deltaIsNegative = -1f; //if the delta is negative, go counter-clockwise //ensure the delta gauge is the right units and granularity - if (!sUnits.equals("-")) { - if (sUnits.equals("mmol")) { + if (!rawData.sUnits.equals("-")) { + if (rawData.sUnits.equals("mmol")) { if (sharedPrefs.getString("delta_granularity", "2").equals("1")) { //low mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_10); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 30f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 30f); //get rid of the sign so it can be converted to float. } if (sharedPrefs.getString("delta_granularity", "2").equals("2")) { //medium mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_05); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 60f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 60f); //get rid of the sign so it can be converted to float. } if (sharedPrefs.getString("delta_granularity", "2").equals("3")) { //high mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_03); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 100f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 100f); //get rid of the sign so it can be converted to float. } } else { if (sharedPrefs.getString("delta_granularity", "2").equals("1")) { //low mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_20); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 1.5f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 1.5f); //get rid of the sign so it can be converted to float. } if (sharedPrefs.getString("delta_granularity", "2").equals("2")) { //medium mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_10); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 3f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 3f); //get rid of the sign so it can be converted to float. } if (sharedPrefs.getString("delta_granularity", "2").equals("3")) { //high mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_5); - deltaRotationAngle = (Float.valueOf(sAvgDelta.substring(1)) * 6f); //get rid of the sign so it can be converted to float. + deltaRotationAngle = (Float.valueOf(rawData.sAvgDelta.substring(1)) * 6f); //get rid of the sign so it can be converted to float. } } } @@ -213,7 +213,7 @@ public class Steampunk extends BaseWatchFace { //top row. large font unless text too big (i.e. detailedIOB) mCOB2.setTextSize(fontLarge); mBasalRate.setTextSize(fontLarge); - if (sIOB2.length() < 7) { + if (rawData.sIOB2.length() < 7) { mIOB2.setTextSize(fontLarge); } else { mIOB2.setTextSize(fontSmall); diff --git a/wear/src/main/res/drawable/ic_aaps_dark.xml b/wear/src/main/res/drawable/ic_aaps_dark.xml new file mode 100644 index 0000000000..4126fec9d9 --- /dev/null +++ b/wear/src/main/res/drawable/ic_aaps_dark.xml @@ -0,0 +1,12 @@ + + + + diff --git a/wear/src/main/res/drawable/ic_aaps_full.xml b/wear/src/main/res/drawable/ic_aaps_full.xml new file mode 100644 index 0000000000..cf9716870c --- /dev/null +++ b/wear/src/main/res/drawable/ic_aaps_full.xml @@ -0,0 +1,12 @@ + + + + diff --git a/wear/src/main/res/drawable/ic_aaps_gray.xml b/wear/src/main/res/drawable/ic_aaps_gray.xml new file mode 100644 index 0000000000..1797ca3c9a --- /dev/null +++ b/wear/src/main/res/drawable/ic_aaps_gray.xml @@ -0,0 +1,12 @@ + + + + diff --git a/wear/src/main/res/drawable/ic_aaps_light.xml b/wear/src/main/res/drawable/ic_aaps_light.xml new file mode 100644 index 0000000000..cf9716870c --- /dev/null +++ b/wear/src/main/res/drawable/ic_aaps_light.xml @@ -0,0 +1,12 @@ + + + + diff --git a/wear/src/main/res/drawable/ic_alert.xml b/wear/src/main/res/drawable/ic_alert.xml new file mode 100644 index 0000000000..d95c322d47 --- /dev/null +++ b/wear/src/main/res/drawable/ic_alert.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_alert_burnin.xml b/wear/src/main/res/drawable/ic_alert_burnin.xml new file mode 100644 index 0000000000..6f7bd23ef0 --- /dev/null +++ b/wear/src/main/res/drawable/ic_alert_burnin.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_alert_variant_outline.xml b/wear/src/main/res/drawable/ic_battery_alert_variant_outline.xml new file mode 100644 index 0000000000..516ae49c17 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_alert_variant_outline.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless.xml new file mode 100644 index 0000000000..bd6bee5fb1 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_10.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_10.xml new file mode 100644 index 0000000000..932c4e5930 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_10.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_10_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_10_burnin.xml new file mode 100644 index 0000000000..a47508a1a9 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_10_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_20.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_20.xml new file mode 100644 index 0000000000..00af480fcd --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_20.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_20_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_20_burnin.xml new file mode 100644 index 0000000000..e08ff88448 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_20_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_30.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_30.xml new file mode 100644 index 0000000000..592d0f380c --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_30.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_30_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_30_burnin.xml new file mode 100644 index 0000000000..eb36861925 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_30_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_40.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_40.xml new file mode 100644 index 0000000000..f94f85a69b --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_40.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_40_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_40_burnin.xml new file mode 100644 index 0000000000..8980db2391 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_40_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_50.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_50.xml new file mode 100644 index 0000000000..f6a4148589 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_50.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_50_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_50_burnin.xml new file mode 100644 index 0000000000..9a9ee645dc --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_50_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_60.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_60.xml new file mode 100644 index 0000000000..29192af0ed --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_60.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_60_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_60_burnin.xml new file mode 100644 index 0000000000..d5191601a6 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_60_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_70.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_70.xml new file mode 100644 index 0000000000..02c0ae7f58 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_70.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_70_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_70_burnin.xml new file mode 100644 index 0000000000..9efc928672 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_70_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_80.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_80.xml new file mode 100644 index 0000000000..21e44f7105 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_80.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_80_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_80_burnin.xml new file mode 100644 index 0000000000..9f212fad87 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_80_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_90.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_90.xml new file mode 100644 index 0000000000..1004964eb2 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_90.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_90_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_90_burnin.xml new file mode 100644 index 0000000000..b6b371fa20 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_90_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_burnin.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_burnin.xml new file mode 100644 index 0000000000..fd11b20ac9 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_battery_charging_wireless_outline.xml b/wear/src/main/res/drawable/ic_battery_charging_wireless_outline.xml new file mode 100644 index 0000000000..5fa535e117 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_charging_wireless_outline.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_outline.xml b/wear/src/main/res/drawable/ic_battery_outline.xml new file mode 100644 index 0000000000..c066289e18 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_outline.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_unknown.xml b/wear/src/main/res/drawable/ic_battery_unknown.xml new file mode 100644 index 0000000000..0ac1d8a740 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_unknown.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_battery_unknown_burnin.xml b/wear/src/main/res/drawable/ic_battery_unknown_burnin.xml new file mode 100644 index 0000000000..e6271cce70 --- /dev/null +++ b/wear/src/main/res/drawable/ic_battery_unknown_burnin.xml @@ -0,0 +1,10 @@ + + + diff --git a/wear/src/main/res/drawable/ic_br_cob_iob.xml b/wear/src/main/res/drawable/ic_br_cob_iob.xml new file mode 100644 index 0000000000..39ab3af490 --- /dev/null +++ b/wear/src/main/res/drawable/ic_br_cob_iob.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_carbs.xml b/wear/src/main/res/drawable/ic_carbs.xml new file mode 100644 index 0000000000..a1ac753064 --- /dev/null +++ b/wear/src/main/res/drawable/ic_carbs.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_cob_detailed.xml b/wear/src/main/res/drawable/ic_cob_detailed.xml new file mode 100644 index 0000000000..fc92be1910 --- /dev/null +++ b/wear/src/main/res/drawable/ic_cob_detailed.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_cob_iob.xml b/wear/src/main/res/drawable/ic_cob_iob.xml new file mode 100644 index 0000000000..f029cf0d49 --- /dev/null +++ b/wear/src/main/res/drawable/ic_cob_iob.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_ins.xml b/wear/src/main/res/drawable/ic_ins.xml new file mode 100644 index 0000000000..9fbb294190 --- /dev/null +++ b/wear/src/main/res/drawable/ic_ins.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_ins_burnin.xml b/wear/src/main/res/drawable/ic_ins_burnin.xml new file mode 100644 index 0000000000..8a3a16c64b --- /dev/null +++ b/wear/src/main/res/drawable/ic_ins_burnin.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_iob_detailed.xml b/wear/src/main/res/drawable/ic_iob_detailed.xml new file mode 100644 index 0000000000..95974f2fed --- /dev/null +++ b/wear/src/main/res/drawable/ic_iob_detailed.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_sgv.xml b/wear/src/main/res/drawable/ic_sgv.xml new file mode 100644 index 0000000000..e7aefdd542 --- /dev/null +++ b/wear/src/main/res/drawable/ic_sgv.xml @@ -0,0 +1,13 @@ + + + diff --git a/wear/src/main/res/drawable/ic_sync_alert.xml b/wear/src/main/res/drawable/ic_sync_alert.xml new file mode 100644 index 0000000000..bd4ca18d5f --- /dev/null +++ b/wear/src/main/res/drawable/ic_sync_alert.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml index d50cb015e6..ca24978853 100644 --- a/wear/src/main/res/values/strings.xml +++ b/wear/src/main/res/values/strings.xml @@ -46,6 +46,25 @@ 4 + + Default + Menu + Wizard + Bolus + eCarb + Status + None + + + + default + menu + wizard + bolus + ecarb + status + none + AAPS AAPS(Large) @@ -53,4 +72,13 @@ AAPS(NoChart) AAPS(Circle) + + No data! + Old data! + Since %1$s + Sync with AAPS! + + No data received since %1$s! Check if AAPS on the phone sends data to watch + AAPS data is %1$s old! Check your sensor, xDrip+, NS, AAPS config or other! + diff --git a/wear/src/main/res/xml/preferences.xml b/wear/src/main/res/xml/preferences.xml index 0befaddfd0..aa8a3f6ba2 100644 --- a/wear/src/main/res/xml/preferences.xml +++ b/wear/src/main/res/xml/preferences.xml @@ -209,6 +209,20 @@ android:title="Wizard Percentage" app:wear_iconOff="@drawable/settings_off" app:wear_iconOn="@drawable/settings_on"/> + + Date: Fri, 15 Nov 2019 23:33:23 +0000 Subject: [PATCH 23/56] Reset VERSION number in build.gradle --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4ad49d22ba..94aaa78834 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,7 @@ android { targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "medtronic-2.5.1.1" + version "2.5.1" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' From 5d768bd3680005162d69926f184e0b25b53d8a33 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 20 Nov 2019 00:20:08 +0100 Subject: [PATCH 24/56] Move plugin related code from preferences to plugin --- .../activities/PreferencesActivity.java | 92 ++++--------------- .../androidaps/interfaces/PluginBase.java | 9 ++ .../general/nsclient/NSClientPlugin.java | 20 ++++ .../SmsCommunicatorPlugin.java | 60 +++++++++++- .../general/tidepool/TidepoolPlugin.kt | 12 ++- .../plugins/pump/danaRS/DanaRSPlugin.java | 10 ++ 6 files changed, 123 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index bf69624c40..f29d2af429 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -9,28 +9,28 @@ import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.text.TextUtils; import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; -import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; +import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; +import info.nightscout.androidaps.plugins.general.wear.WearPlugin; +import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; +import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; @@ -43,16 +43,9 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.general.wear.WearPlugin; -import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; -import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; - -import com.andreabaccega.widget.ValidatingEditTextPreference; public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { MyPreferenceFragment myPreferenceFragment; @@ -83,7 +76,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre if (key.equals(MainApp.gs(R.string.key_openapsama_useautosens)) && SP.getBoolean(R.string.key_openapsama_useautosens, false)) { OKDialog.show(this, MainApp.gs(R.string.configbuilder_sensitivity), MainApp.gs(R.string.sensitivity_warning), null); } - updatePrefSummary(myPreferenceFragment.getPreference(key)); + updatePrefSummary(myPreferenceFragment.findPreference(key)); } private static void updatePrefSummary(Preference pref) { @@ -95,13 +88,13 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre EditTextPreference editTextPref = (EditTextPreference) pref; if (pref.getKey().contains("password") || pref.getKey().contains("secret")) { pref.setSummary("******"); - } else if (pref.getKey().equals(MainApp.gs(R.string.key_danars_name))) { - pref.setSummary(SP.getString(R.string.key_danars_name, "")); } else if (editTextPref.getText() != null) { ((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage()); pref.setSummary(editTextPref.getText()); - } else if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { - pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); + } else { + for (PluginBase plugin : MainApp.getPluginsList()) { + plugin.updatePreferenceSummary(pref); + } } } } @@ -201,58 +194,11 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(StatuslinePlugin.getPlugin(), PluginType.GENERAL); } - if (Config.NSCLIENT) { - PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) findPreference(getString(R.string.key_advancedsettings)); - if (scrnAdvancedSettings != null) { - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_warning))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_critical))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_warning))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_critical))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights_extended))); - } - } - initSummary(getPreferenceScreen()); - final Preference tidepoolTestLogin = findPreference(MainApp.gs(R.string.key_tidepool_test_login)); - if (tidepoolTestLogin != null) - tidepoolTestLogin.setOnPreferenceClickListener(preference -> { - TidepoolUploader.INSTANCE.testLogin(getActivity()); - return false; - }); - - final ValidatingEditTextPreference distance = (ValidatingEditTextPreference)findPreference(getString(R.string.key_smscommunicator_remotebolusmindistance)); - final EditTextPreference allowedNumbers = (EditTextPreference)findPreference(getString(R.string.key_smscommunicator_allowednumbers)); - if (distance != null && allowedNumbers != null) { - if (!SmsCommunicatorPlugin.areMoreNumbers(allowedNumbers.getText())) { - distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance) - + ".\n" - + getString(R.string.smscommunicator_remotebolusmindistance_caveat)); - distance.setEnabled(false); - } else { - distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)); - distance.setEnabled(true); - } - - allowedNumbers.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (!SmsCommunicatorPlugin.areMoreNumbers(((String)newValue))) { - distance.setText(String.valueOf(Constants.remoteBolusMinDistance/(60 * 1000L))); - distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance) - + ".\n" - + getString(R.string.smscommunicator_remotebolusmindistance_caveat)); - distance.setEnabled(false); - } else { - distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)); - distance.setEnabled(true); - } - return true; - } - }); + for (PluginBase plugin : MainApp.getPluginsList()) { + plugin.preprocessPreferences(this); } - } @Override @@ -260,9 +206,5 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre super.onSaveInstanceState(outState); outState.putInt("id", id); } - - public Preference getPreference(String key) { - return findPreference(key); - } } } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java index f7c8e9ada4..8de88e1e0e 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -1,10 +1,13 @@ package info.nightscout.androidaps.interfaces; import android.os.SystemClock; +import android.preference.Preference; +import android.preference.PreferenceFragment; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -215,4 +218,10 @@ public abstract class PluginBase { protected void onStateChange(PluginType type, State oldState, State newState) { } + + public void preprocessPreferences(@NotNull final PreferenceFragment preferenceFragment) { + } + + public void updatePreferenceSummary(@NotNull final Preference pref) { + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java index ae5d090211..a4ea283c8b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java @@ -7,9 +7,12 @@ import android.content.ServiceConnection; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; import android.text.Html; import android.text.Spanned; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -157,6 +160,23 @@ public class NSClientPlugin extends PluginBase { super.onStop(); } + @Override + public void preprocessPreferences(@NotNull PreferenceFragment preferenceFragment) { + super.preprocessPreferences(preferenceFragment); + + if (Config.NSCLIENT) { + PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) preferenceFragment.findPreference(MainApp.gs(R.string.key_advancedsettings)); + if (scrnAdvancedSettings != null) { + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_warning))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_critical))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_warning))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_critical))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights_extended))); + } + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java index f40d60523c..80079b67ff 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java @@ -2,10 +2,17 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator; import android.content.Intent; import android.os.Bundle; +import android.preference.EditTextPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; import android.telephony.SmsManager; import android.telephony.SmsMessage; +import android.text.TextUtils; + +import com.andreabaccega.widget.ValidatingEditTextPreference; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,6 +118,51 @@ public class SmsCommunicatorPlugin extends PluginBase { super.onStop(); } + @Override + public void preprocessPreferences(@NotNull final PreferenceFragment preferenceFragment) { + super.preprocessPreferences(preferenceFragment); + + final ValidatingEditTextPreference distance = (ValidatingEditTextPreference) preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)); + final EditTextPreference allowedNumbers = (EditTextPreference) preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)); + if (distance != null && allowedNumbers != null) { + if (!areMoreNumbers(allowedNumbers.getText())) { + distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)); + distance.setEnabled(false); + } else { + distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance)); + distance.setEnabled(true); + } + + allowedNumbers.setOnPreferenceChangeListener((preference, newValue) -> { + if (!areMoreNumbers(((String) newValue))) { + distance.setText(String.valueOf(Constants.remoteBolusMinDistance / (60 * 1000L))); + distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)); + distance.setEnabled(false); + } else { + distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance)); + distance.setEnabled(true); + } + return true; + }); + } + } + + @Override + public void updatePreferenceSummary(@NotNull final Preference pref) { + super.updatePreferenceSummary(pref); + + if (pref instanceof EditTextPreference) { + EditTextPreference editTextPref = (EditTextPreference) pref; + if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { + pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); + } + } + } + private void processSettings(final EventPreferenceChange ev) { if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) { String settings = SP.getString(R.string.key_smscommunicator_allowednumbers, ""); @@ -853,7 +905,7 @@ public class SmsCommunicatorPlugin extends PluginBase { private void processSMS(String[] splitted, Sms receivedSms) { boolean isStop = splitted[1].equalsIgnoreCase("STOP") - || splitted[1].equalsIgnoreCase("DISABLE"); + || splitted[1].equalsIgnoreCase("DISABLE"); if (isStop) { String passCode = generatePasscode(); @@ -965,9 +1017,9 @@ public class SmsCommunicatorPlugin extends PluginBase { for (String number : substrings) { String cleaned = number.replaceAll("\\s+", ""); - if (cleaned.length()<4) continue; - if (cleaned.substring(0,1).compareTo("+")!=0) continue; - cleaned = cleaned.replace("+",""); + if (cleaned.length() < 4) continue; + if (cleaned.substring(0, 1).compareTo("+") != 0) continue; + cleaned = cleaned.replace("+", ""); if (!cleaned.matches("[0-9]+")) continue; countNumbers++; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt index 78ad0ab37d..e8971ab9d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.general.tidepool +import android.preference.PreferenceFragment import android.text.Spanned import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp @@ -34,7 +35,6 @@ object TidepoolPlugin : PluginBase(PluginDescription() .preferencesId(R.xml.pref_tidepool) .description(R.string.description_tidepool) ) { - private val log = LoggerFactory.getLogger(L.TIDEPOOL) private var disposable: CompositeDisposable = CompositeDisposable() @@ -111,6 +111,16 @@ object TidepoolPlugin : PluginBase(PluginDescription() super.onStop() } + override fun preprocessPreferences(preferenceFragment: PreferenceFragment) { + super.preprocessPreferences(preferenceFragment) + + val tidepoolTestLogin = preferenceFragment.findPreference(MainApp.gs(R.string.key_tidepool_test_login)) + tidepoolTestLogin?.setOnPreferenceClickListener { + TidepoolUploader.testLogin(preferenceFragment.getActivity()) + false + } + } + private fun doUpload() = when (TidepoolUploader.connectionStatus) { TidepoolUploader.ConnectionStatus.FAILED -> {} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java index 7b2668d45c..55be88996b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java @@ -5,10 +5,12 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.preference.Preference; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; +import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -110,6 +112,14 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte } } + @Override + public void updatePreferenceSummary(@NotNull Preference pref) { + super.updatePreferenceSummary(pref); + + if (pref.getKey().equals(MainApp.gs(R.string.key_danars_name))) + pref.setSummary(SP.getString(R.string.key_danars_name, "")); + } + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); From 1f4f3fb6873557a104f08b7d39fd7202ae09c4f1 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Wed, 20 Nov 2019 16:44:33 +0100 Subject: [PATCH 25/56] Added the new MEAL feature to tests --- .../SmsCommunicatorPluginTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index bc928ded85..6e83032504 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -661,6 +661,35 @@ public class SmsCommunicatorPluginTest { Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.messages.get(0).text); Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.messages.get(1).text); when(pump.isSuspended()).thenReturn(false); + + //BOLUS 1 a + smsCommunicatorPlugin.messages = new ArrayList<>(); + sms = new Sms("1234", "BOLUS 1 a"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("BOLUS 1 a", smsCommunicatorPlugin.messages.get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + + //BOLUS 1 MEAL + smsCommunicatorPlugin.messages = new ArrayList<>(); + sms = new Sms("1234", "BOLUS 1 MEAL"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.messages.get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To deliver meal bolus 1.00U reply with code")); + passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); + Assert.assertEquals("Meal Bolus 1.00U delivered successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + + //BOLUS 1 MEAL (Suspended pump) + smsCommunicatorPlugin.lastRemoteBolusTime = 0; + when(ConfigBuilderPlugin.getPlugin().getActivePump()).thenReturn(pump); + when(pump.isSuspended()).thenReturn(true); + smsCommunicatorPlugin.messages = new ArrayList<>(); + sms = new Sms("1234", "BOLUS 1 MEAL"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.messages.get(0).text); + Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.messages.get(1).text); + when(pump.isSuspended()).thenReturn(false); } @Test From baf77e2e6748fe2aa6992df302a55aeaf7384914 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 20 Nov 2019 21:50:02 +0100 Subject: [PATCH 26/56] fix tests --- .../plugins/general/smsCommunicator/SmsCommunicatorPlugin.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java index 80079b67ff..5fb583c760 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java @@ -302,6 +302,9 @@ public class SmsCommunicatorPlugin extends PluginBase { else sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); break; + } + // splitted switch to fix bug in mockito + switch (splitted[0].toUpperCase()) { case "CAL": if (!remoteCommandsAllowed) sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); From 2fe276d1013c795dcf50b4cd58d77f0871cd5229 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 20 Nov 2019 22:03:31 +0100 Subject: [PATCH 27/56] accept data 1 month back in Dexcom plugin --- .../plugins/source/SourceDexcomPlugin.kt | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt index dfbe2fff39..633f16cf10 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt @@ -17,6 +17,7 @@ import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.T import org.json.JSONObject import org.slf4j.LoggerFactory @@ -51,7 +52,7 @@ object SourceDexcomPlugin : PluginBase(PluginDescription() } fun findDexcomPackageName(): String? { - val packageManager = MainApp.instance().packageManager; + val packageManager = MainApp.instance().packageManager for (packageInfo in packageManager.getInstalledPackages(0)) { if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName } @@ -64,43 +65,53 @@ object SourceDexcomPlugin : PluginBase(PluginDescription() val sensorType = intent.getStringExtra("sensorType") ?: "" val glucoseValues = intent.getBundleExtra("glucoseValues") for (i in 0 until glucoseValues.size()) { - val glucoseValue = glucoseValues.getBundle(i.toString()) - val bgReading = BgReading() - bgReading.value = glucoseValue!!.getInt("glucoseValue").toDouble() - bgReading.direction = glucoseValue.getString("trendArrow") - bgReading.date = glucoseValue.getLong("timestamp") * 1000 - bgReading.raw = 0.0 - if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) { - if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - NSUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType") - } - if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { - NSUpload.sendToXdrip(bgReading) + glucoseValues.getBundle(i.toString())?.let { glucoseValue -> + val bgReading = BgReading() + bgReading.value = glucoseValue.getInt("glucoseValue").toDouble() + bgReading.direction = glucoseValue.getString("trendArrow") + bgReading.date = glucoseValue.getLong("timestamp") * 1000 + bgReading.raw = 0.0 + if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) { + if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { + NSUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType") + } + if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { + NSUpload.sendToXdrip(bgReading) + } } } } val meters = intent.getBundleExtra("meters") for (i in 0 until meters.size()) { val meter = meters.getBundle(i.toString()) - val timestamp = meter!!.getLong("timestamp") * 1000 - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) != null) continue - val jsonObject = JSONObject() - jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") - jsonObject.put("created_at", DateUtil.toISOString(timestamp)) - jsonObject.put("eventType", CareportalEvent.BGCHECK) - jsonObject.put("glucoseType", "Finger") - jsonObject.put("glucose", meter.getInt("meterValue")) - jsonObject.put("units", Constants.MGDL) - NSUpload.uploadCareportalEntryToNS(jsonObject) + meter?.let { + val timestamp = it.getLong("timestamp") * 1000 + val now = DateUtil.now() + if (timestamp > now - T.months(1).msecs() && timestamp < now) + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) == null) { + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(timestamp)) + jsonObject.put("eventType", CareportalEvent.BGCHECK) + jsonObject.put("glucoseType", "Finger") + jsonObject.put("glucose", meter.getInt("meterValue")) + jsonObject.put("units", Constants.MGDL) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } + } } if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false) && intent.hasExtra("sensorInsertionTime")) { - val sensorInsertionTime = intent.extras!!.getLong("sensorInsertionTime") * 1000 - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { - val jsonObject = JSONObject() - jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") - jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime)) - jsonObject.put("eventType", CareportalEvent.SENSORCHANGE) - NSUpload.uploadCareportalEntryToNS(jsonObject) + intent.extras?.let { + val sensorInsertionTime = it.getLong("sensorInsertionTime") * 1000 + val now = DateUtil.now() + if (sensorInsertionTime > now - T.months(1).msecs() && sensorInsertionTime < now) + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime)) + jsonObject.put("eventType", CareportalEvent.SENSORCHANGE) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } } } } catch (e: Exception) { From 9aa77122ba76ece6d27bf2634203194ed4b5e194 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 21 Nov 2019 00:07:50 +0100 Subject: [PATCH 28/56] Use on Rx for NetworkChange --- .../objectives/ObjectivesFragment.kt | 4 +- .../general/nsclient/NSClientPlugin.java | 6 +-- .../nsclient/NsClientReceiverDelegate.java | 44 ++++--------------- .../receivers/ChargingStateReceiver.java | 2 +- .../receivers/NetworkChangeReceiver.java | 7 +-- .../NsClientReceiverDelegateTest.java | 4 +- 6 files changed, 15 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index aafd877672..5edb199c9a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -204,7 +204,7 @@ class ObjectivesFragment : Fragment() { holder.accomplished.setTextColor(-0x3e3e3f) holder.verify.setOnClickListener { holder.verify.visibility = View.INVISIBLE - NetworkChangeReceiver.fetch() + NetworkChangeReceiver.grabNetworkStatus(context) if (objectives_fake.isChecked) { objective.accomplishedOn = DateUtil.now() scrollToCurrentObjective() @@ -236,7 +236,7 @@ class ObjectivesFragment : Fragment() { } holder.start.setOnClickListener { holder.start.visibility = View.INVISIBLE - NetworkChangeReceiver.fetch() + NetworkChangeReceiver.grabNetworkStatus(context) if (objectives_fake.isChecked) { objective.startedOn = DateUtil.now() scrollToCurrentObjective() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java index ae5d090211..1ad73093c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java @@ -89,7 +89,7 @@ public class NSClientPlugin extends PluginBase { } nsClientReceiverDelegate = - new NsClientReceiverDelegate(MainApp.instance().getApplicationContext()); + new NsClientReceiverDelegate(); } public boolean isAllowed() { @@ -104,7 +104,7 @@ public class NSClientPlugin extends PluginBase { context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); super.onStart(); - nsClientReceiverDelegate.registerReceivers(); + nsClientReceiverDelegate.grabReceiversState(); disposable.add(RxBus.INSTANCE .toObservable(EventNSClientStatus.class) .observeOn(Schedulers.io()) @@ -129,7 +129,6 @@ public class NSClientPlugin extends PluginBase { .subscribe(event -> { if (nsClientService != null) { MainApp.instance().getApplicationContext().unbindService(mConnection); - nsClientReceiverDelegate.unregisterReceivers(); } }, FabricPrivacy::logException) ); @@ -152,7 +151,6 @@ public class NSClientPlugin extends PluginBase { @Override protected void onStop() { MainApp.instance().getApplicationContext().unbindService(mConnection); - nsClientReceiverDelegate.unregisterReceivers(); disposable.clear(); super.onStop(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java index c68750d828..fd05b133f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java @@ -18,44 +18,19 @@ import info.nightscout.androidaps.utils.SP; class NsClientReceiverDelegate { - private final Context context; - - private NetworkChangeReceiver networkChangeReceiver = new NetworkChangeReceiver(); - private ChargingStateReceiver chargingStateReceiver = new ChargingStateReceiver(); - private boolean allowedChargingState = true; private boolean allowedNetworkState = true; boolean allowed = true; - NsClientReceiverDelegate(Context context) { - this.context = context; - } - - void registerReceivers() { + void grabReceiversState() { Context context = MainApp.instance().getApplicationContext(); - // register NetworkChangeReceiver --> https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html - // Nougat is not providing Connectivity-Action anymore ;-( - context.registerReceiver(networkChangeReceiver, - new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - context.registerReceiver(networkChangeReceiver, - new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); - EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(context); - if (event != null) - RxBus.INSTANCE.send(event); + EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(context); + if (event != null) RxBus.INSTANCE.send(event); - context.registerReceiver(chargingStateReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + EventChargingState eventChargingState = ChargingStateReceiver.grabChargingState(context); + if (eventChargingState != null) RxBus.INSTANCE.send(eventChargingState); - EventChargingState eventChargingState = chargingStateReceiver.grabChargingState(context); - if (eventChargingState != null) - RxBus.INSTANCE.send(eventChargingState); - - } - - void unregisterReceivers() { - context.unregisterReceiver(networkChangeReceiver); - context.unregisterReceiver(chargingStateReceiver); } void onStatusEvent(EventPreferenceChange ev) { @@ -63,11 +38,11 @@ class NsClientReceiverDelegate { ev.isChanged(R.string.key_ns_wifi_ssids) || ev.isChanged(R.string.key_ns_allowroaming) ) { - EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext()); + EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext()); if (event != null) RxBus.INSTANCE.send(event); } else if (ev.isChanged(R.string.key_ns_chargingonly)) { - EventChargingState event = chargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext()); + EventChargingState event = ChargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext()); if (event != null) RxBus.INSTANCE.send(event); } @@ -91,7 +66,7 @@ class NsClientReceiverDelegate { } } - void processStateChange() { + private void processStateChange() { boolean newAllowedState = allowedChargingState && allowedNetworkState; if (newAllowedState != allowed) { allowed = newAllowedState; @@ -101,7 +76,6 @@ class NsClientReceiverDelegate { boolean calculateStatus(final EventChargingState ev) { boolean chargingOnly = SP.getBoolean(R.string.key_ns_chargingonly, false); - boolean newAllowedState = true; if (!ev.isCharging() && chargingOnly) { @@ -129,8 +103,6 @@ class NsClientReceiverDelegate { } } - return newAllowedState; } - } diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java index 9af15e9ddc..cb49d8cf4e 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java @@ -21,7 +21,7 @@ public class ChargingStateReceiver extends BroadcastReceiver { lastEvent = event; } - public EventChargingState grabChargingState(Context context) { + public static EventChargingState grabChargingState(Context context) { BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE); if (bm == null) diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java index 3f12d75fce..19cf12bc8a 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java @@ -26,11 +26,6 @@ public class NetworkChangeReceiver extends BroadcastReceiver { public static final NetworkChangeReceiver instance = new NetworkChangeReceiver(); - // TODO: Split NSClient into network state component that can be used by several plugins and logic for plugin - public static void fetch() { - new NetworkChangeReceiver().grabNetworkStatus(MainApp.instance().getApplicationContext()); - } - @Override public void onReceive(final Context context, final Intent intent) { EventNetworkChange event = grabNetworkStatus(context); @@ -39,7 +34,7 @@ public class NetworkChangeReceiver extends BroadcastReceiver { } @Nullable - public EventNetworkChange grabNetworkStatus(final Context context) { + public static EventNetworkChange grabNetworkStatus(final Context context) { EventNetworkChange event = new EventNetworkChange(); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.java index ad172f13e9..b0fde44b08 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.java @@ -33,9 +33,7 @@ public class NsClientReceiverDelegateTest { AAPSMocker.mockMainApp(); AAPSMocker.mockApplicationContext(); - Context context = MainApp.instance().getApplicationContext(); - - sut = new NsClientReceiverDelegate(context); + sut = new NsClientReceiverDelegate(); } @Test From b2079aa31606b4bbb718bd2df45fb531bfadd5a7 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 21 Nov 2019 00:20:01 +0100 Subject: [PATCH 29/56] Warn 12h before DST change --- .../plugins/constraints/dstHelper/DstHelperPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java index e026d6589d..828fa1b86f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java @@ -22,7 +22,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific public class DstHelperPlugin extends PluginBase implements ConstraintsInterface { public static final int DISABLE_TIMEFRAME_HOURS = -3; - public static final int WARN_PRIOR_TIMEFRAME_HOURS = 24; + public static final int WARN_PRIOR_TIMEFRAME_HOURS = 12; private static Logger log = LoggerFactory.getLogger(L.CONSTRAINTS); static DstHelperPlugin plugin = null; From 07ca4b65243865cdd32dec8ba59e776a04e1448e Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Wed, 20 Nov 2019 00:18:30 +0100 Subject: [PATCH 30/56] Unit test configuration for Wear OS, first tests --- wear/build.gradle | 15 ++ .../interaction/utils/SafeParseTest.java | 128 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 wear/src/test/java/info/nightscout/androidaps/interaction/utils/SafeParseTest.java diff --git a/wear/build.gradle b/wear/build.gradle index 7e6500fe56..46161f6a5a 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.application' ext { wearableVersion = "2.4.0" playServicesWearable = "17.0.0" + powermockVersion = "1.7.3" } def generateGitBuild = { -> @@ -101,4 +102,18 @@ dependencies { implementation 'androidx.wear:wear:1.0.0' implementation('me.denley.wearpreferenceactivity:wearpreferenceactivity:0.5.0') implementation('com.github.lecho:hellocharts-library:1.5.8@aar') + + testImplementation "junit:junit:4.12" + testImplementation "org.json:json:20140107" + testImplementation "org.mockito:mockito-core:2.8.47" + testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" + testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" + testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" + testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" + testImplementation "joda-time:joda-time:2.9.9" + testImplementation("com.google.truth:truth:0.39") { + exclude group: "com.google.guava", module: "guava" + } + testImplementation "org.skyscreamer:jsonassert:1.5.0" + testImplementation "org.hamcrest:hamcrest-all:1.3" } diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/SafeParseTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/SafeParseTest.java new file mode 100644 index 0000000000..62a71e6420 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/SafeParseTest.java @@ -0,0 +1,128 @@ +package info.nightscout.androidaps.interaction.utils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by dlvoy on 21.11.2019. + */ +@RunWith(PowerMockRunner.class) +public class SafeParseTest { + + @Test + public void stringToDoubleTest() { + // correct values + assertThat(0.1234, is(SafeParse.stringToDouble("0.1234"))); + assertThat(0.1234, is(SafeParse.stringToDouble("0,1234"))); + assertThat(0.5436564812, is(SafeParse.stringToDouble(".5436564812"))); + assertThat(0.5436564812, is(SafeParse.stringToDouble(",5436564812"))); + assertThat(1000500100900.0, is(SafeParse.stringToDouble("1000500100900"))); + assertThat(42.0, is(SafeParse.stringToDouble("42"))); + + // units or other extra values are not permitted + assertThat(0.0, is(SafeParse.stringToDouble("12 U/h"))); + + // strings are not parsable + assertThat(0.0, is(SafeParse.stringToDouble("ala ma kota"))); + + // separator errors + assertThat(0.0, is(SafeParse.stringToDouble("0.1234.5678"))); + assertThat(0.0, is(SafeParse.stringToDouble("0,1234,5678"))); + + // various emptiness + assertThat(0.0, is(SafeParse.stringToDouble(""))); + assertThat(0.0, is(SafeParse.stringToDouble(" "))); + assertThat(0.0, is(SafeParse.stringToDouble("\n\r"))); + } + + @Test + public void stringToIntTest() { + // correct values + assertThat(1052934, is(SafeParse.stringToInt("1052934"))); + assertThat(-42, is(SafeParse.stringToInt("-42"))); + assertThat(2147483647, is(SafeParse.stringToInt("2147483647"))); + assertThat(-2147483648, is(SafeParse.stringToInt("-2147483648"))); + + // outside Integer range + assertThat(0, is(SafeParse.stringToInt("2147483648"))); + assertThat(0, is(SafeParse.stringToInt("-2147483649"))); + + // units or other extra values are not permitted + assertThat(0, is(SafeParse.stringToInt("12 U/h"))); + assertThat(0, is(SafeParse.stringToInt("0.1234"))); + assertThat(0, is(SafeParse.stringToInt("0,1234"))); + assertThat(0, is(SafeParse.stringToInt(".5436564812"))); + assertThat(0, is(SafeParse.stringToInt(",5436564812"))); + assertThat(0, is(SafeParse.stringToInt("42.1234"))); + assertThat(0, is(SafeParse.stringToInt("42,1234"))); + assertThat(0, is(SafeParse.stringToInt("3212.5436564812"))); + assertThat(0, is(SafeParse.stringToInt("3212,5436564812"))); + assertThat(0, is(SafeParse.stringToInt("1000500100900"))); + + // strings are not parsable + assertThat(0, is(SafeParse.stringToInt("ala ma kota"))); + + // various emptiness + assertThat(0, is(SafeParse.stringToInt(""))); + assertThat(0, is(SafeParse.stringToInt(" "))); + assertThat(0, is(SafeParse.stringToInt("\n\r"))); + } + + @Test + public void stringToLongTest() { + // correct values + assertThat(1052934L, is(SafeParse.stringToLong("1052934"))); + assertThat(-42L, is(SafeParse.stringToLong("-42"))); + assertThat(2147483647L, is(SafeParse.stringToLong("2147483647"))); + assertThat(-2147483648L, is(SafeParse.stringToLong("-2147483648"))); + assertThat(1000500100900L, is(SafeParse.stringToLong("1000500100900"))); + + // outside Integer range + assertThat(2147483648L, is(SafeParse.stringToLong("2147483648"))); + assertThat(-2147483649L, is(SafeParse.stringToLong("-2147483649"))); + + // units or other extra values are not permitted + assertThat(0L, is(SafeParse.stringToLong("12 U/h"))); + assertThat(0L, is(SafeParse.stringToLong("0.1234"))); + assertThat(0L, is(SafeParse.stringToLong("0,1234"))); + assertThat(0L, is(SafeParse.stringToLong(".5436564812"))); + assertThat(0L, is(SafeParse.stringToLong(",5436564812"))); + assertThat(0L, is(SafeParse.stringToLong("42.1234"))); + assertThat(0L, is(SafeParse.stringToLong("42,1234"))); + assertThat(0L, is(SafeParse.stringToLong("3212.5436564812"))); + assertThat(0L, is(SafeParse.stringToLong("3212,5436564812"))); + + // strings are not parsable + assertThat(0L, is(SafeParse.stringToLong("ala ma kota"))); + + // various emptiness + assertThat(0L, is(SafeParse.stringToLong(""))); + assertThat(0L, is(SafeParse.stringToLong(" "))); + assertThat(0L, is(SafeParse.stringToLong("\n\r"))); + } + + @Test(expected=NullPointerException.class) + public void stringToDoubleNullTest() { + SafeParse.stringToDouble(null); + } + + @Test(expected=NullPointerException.class) + public void stringToIntNullTest() { + SafeParse.stringToInt(null); + } + + @Test(expected=NullPointerException.class) + public void stringToLongNullTest() { + SafeParse.stringToLong(null); + } + + @Before + public void prepareMock() { + + } +} From d227013496d866057bb870df6ab963aaa0aa58c2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 00:12:46 +0100 Subject: [PATCH 31/56] SmsCommunicatorPlugin -> kt --- .../general/smsCommunicator/AuthRequest.java | 2 +- .../plugins/general/smsCommunicator/Sms.java | 2 +- .../SmsCommunicatorFragment.java | 8 +- .../SmsCommunicatorPlugin.java | 1031 ----------------- .../smsCommunicator/SmsCommunicatorPlugin.kt | 812 +++++++++++++ app/src/main/res/values/strings.xml | 2 +- app/src/test/java/info/AAPSMocker.java | 3 + .../smsCommunicator/AuthRequestTest.java | 8 +- .../SmsCommunicatorPluginTest.java | 500 ++++---- 9 files changed, 1070 insertions(+), 1298 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java index 4b05072eb1..45cd824743 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java @@ -8,7 +8,7 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.utils.DateUtil; -class AuthRequest { +public class AuthRequest { private static Logger log = LoggerFactory.getLogger(L.SMS); Sms requester; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java index 2eedfa0a51..8efeb909fc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java @@ -5,7 +5,7 @@ import android.telephony.SmsMessage; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.utils.DateUtil; -class Sms { +public class Sms { String phoneNumber; String text; long date; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java index 31f335a7cf..7c147a2409 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java @@ -62,14 +62,14 @@ public class SmsCommunicatorFragment extends Fragment { return (int) (object1.date - object2.date); } } - Collections.sort(SmsCommunicatorPlugin.getPlugin().messages, new CustomComparator()); + Collections.sort(SmsCommunicatorPlugin.getPlugin().getMessages(), new CustomComparator()); int messagesToShow = 40; - int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().messages.size() - messagesToShow); + int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().getMessages().size() - messagesToShow); String logText = ""; - for (int x = start; x < SmsCommunicatorPlugin.getPlugin().messages.size(); x++) { - Sms sms = SmsCommunicatorPlugin.getPlugin().messages.get(x); + for (int x = start; x < SmsCommunicatorPlugin.getPlugin().getMessages().size(); x++) { + Sms sms = SmsCommunicatorPlugin.getPlugin().getMessages().get(x); if (sms.ignored) { logText += DateUtil.timeString(sms.date) + " <<< " + "░ " + sms.phoneNumber + " " + sms.text + "
"; } else if (sms.received) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java deleted file mode 100644 index 5fb583c760..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ /dev/null @@ -1,1031 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -import android.content.Intent; -import android.os.Bundle; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.telephony.SmsManager; -import android.telephony.SmsMessage; -import android.text.TextUtils; - -import com.andreabaccega.widget.ValidatingEditTextPreference; - -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.ProfileStore; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.XdripCalibrations; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Created by mike on 05.08.2016. - */ -public class SmsCommunicatorPlugin extends PluginBase { - private static Logger log = LoggerFactory.getLogger(L.SMS); - private CompositeDisposable disposable = new CompositeDisposable(); - - private static SmsCommunicatorPlugin smsCommunicatorPlugin; - - public static SmsCommunicatorPlugin getPlugin() { - - if (smsCommunicatorPlugin == null) { - smsCommunicatorPlugin = new SmsCommunicatorPlugin(); - } - return smsCommunicatorPlugin; - } - - List allowedNumbers = new ArrayList<>(); - - AuthRequest messageToConfirm = null; - - long lastRemoteBolusTime = 0; - - ArrayList messages = new ArrayList<>(); - - SmsCommunicatorPlugin() { - super(new PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(SmsCommunicatorFragment.class.getName()) - .pluginName(R.string.smscommunicator) - .shortName(R.string.smscommunicator_shortname) - .preferencesId(R.xml.pref_smscommunicator) - .description(R.string.description_sms_communicator) - ); - processSettings(null); - } - - @Override - protected void onStart() { - super.onStart(); - disposable.add(RxBus.INSTANCE - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - processSettings(event); - }, FabricPrivacy::logException) - ); - } - - @Override - protected void onStop() { - disposable.clear(); - super.onStop(); - } - - @Override - public void preprocessPreferences(@NotNull final PreferenceFragment preferenceFragment) { - super.preprocessPreferences(preferenceFragment); - - final ValidatingEditTextPreference distance = (ValidatingEditTextPreference) preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)); - final EditTextPreference allowedNumbers = (EditTextPreference) preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)); - if (distance != null && allowedNumbers != null) { - if (!areMoreNumbers(allowedNumbers.getText())) { - distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance) - + ".\n" - + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)); - distance.setEnabled(false); - } else { - distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance)); - distance.setEnabled(true); - } - - allowedNumbers.setOnPreferenceChangeListener((preference, newValue) -> { - if (!areMoreNumbers(((String) newValue))) { - distance.setText(String.valueOf(Constants.remoteBolusMinDistance / (60 * 1000L))); - distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance) - + ".\n" - + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)); - distance.setEnabled(false); - } else { - distance.setTitle(MainApp.gs(R.string.smscommunicator_remotebolusmindistance)); - distance.setEnabled(true); - } - return true; - }); - } - } - - @Override - public void updatePreferenceSummary(@NotNull final Preference pref) { - super.updatePreferenceSummary(pref); - - if (pref instanceof EditTextPreference) { - EditTextPreference editTextPref = (EditTextPreference) pref; - if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { - pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); - } - } - } - - private void processSettings(final EventPreferenceChange ev) { - if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) { - String settings = SP.getString(R.string.key_smscommunicator_allowednumbers, ""); - - allowedNumbers.clear(); - String[] substrings = settings.split(";"); - for (String number : substrings) { - String cleaned = number.replaceAll("\\s+", ""); - allowedNumbers.add(cleaned); - log.debug("Found allowed number: " + cleaned); - } - } - } - - boolean isCommand(String command, String number) { - switch (command.toUpperCase()) { - case "BG": - case "LOOP": - case "TREATMENTS": - case "NSCLIENT": - case "PUMP": - case "BASAL": - case "BOLUS": - case "EXTENDED": - case "CAL": - case "PROFILE": - case "TARGET": - case "SMS": - return true; - } - if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(number)) - return true; - return false; - } - - boolean isAllowedNumber(String number) { - for (String num : allowedNumbers) { - if (num.equals(number)) return true; - } - return false; - } - - public void handleNewData(Intent intent) { - Bundle bundle = intent.getExtras(); - if (bundle == null) return; - - Object[] pdus = (Object[]) bundle.get("pdus"); - if (pdus != null) { - // For every SMS message received - for (Object pdu : pdus) { - SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu); - processSms(new Sms(message)); - } - } - } - - void processSms(final Sms receivedSms) { - if (!isEnabled(PluginType.GENERAL)) { - log.debug("Ignoring SMS. Plugin disabled."); - return; - } - if (!isAllowedNumber(receivedSms.phoneNumber)) { - log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed"); - receivedSms.ignored = true; - messages.add(receivedSms); - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - return; - } - - messages.add(receivedSms); - log.debug(receivedSms.toString()); - - String[] splitted = receivedSms.text.split("\\s+"); - boolean remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); - - if (splitted.length > 0 && isCommand(splitted[0].toUpperCase(), receivedSms.phoneNumber)) { - switch (splitted[0].toUpperCase()) { - case "BG": - processBG(splitted, receivedSms); - break; - case "LOOP": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processLOOP(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "TREATMENTS": - if (splitted.length == 2) - processTREATMENTS(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "NSCLIENT": - if (splitted.length == 2) - processNSCLIENT(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "PUMP": - processPUMP(splitted, receivedSms); - break; - case "PROFILE": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processPROFILE(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "BASAL": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processBASAL(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "EXTENDED": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processEXTENDED(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "BOLUS": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)); - else if (splitted.length == 2 && ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.pumpsuspended)); - else if (splitted.length == 2 || splitted.length == 3) - processBOLUS(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - } - // splitted switch to fix bug in mockito - switch (splitted[0].toUpperCase()) { - case "CAL": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2) - processCAL(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "TARGET": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2) - processTARGET(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "SMS": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2) - processSMS(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - default: // expect passCode here - if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(receivedSms.phoneNumber)) { - messageToConfirm.action(splitted[0]); - messageToConfirm = null; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); - break; - } - } - - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - } - - @SuppressWarnings("unused") - private void processBG(String[] splitted, Sms receivedSms) { - BgReading actualBG = DatabaseHelper.actualBg(); - BgReading lastBG = DatabaseHelper.lastBg(); - - String reply = ""; - - String units = ProfileFunctions.getInstance().getProfileUnits(); - - if (actualBG != null) { - reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "; - } else if (lastBG != null) { - Long agoMsec = System.currentTimeMillis() - lastBG.date; - int agoMin = (int) (agoMsec / 60d / 1000d); - reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", "; - } - GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); - if (glucoseStatus != null) - reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "; - - TreatmentsPlugin.getPlugin().updateTotalIOBTreatments(); - IobTotal bolusIob = TreatmentsPlugin.getPlugin().getLastCalculationTreatments().round(); - TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals(); - IobTotal basalIob = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals().round(); - - String cobText = MainApp.gs(R.string.value_unavailable_short); - CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB"); - - reply += MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" - + MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " - + MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), " - + MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString(); - - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - receivedSms.processed = true; - } - - private void processLOOP(String[] splitted, Sms receivedSms) { - String reply; - switch (splitted[1].toUpperCase()) { - case "DISABLE": - case "STOP": - LoopPlugin loopPlugin = LoopPlugin.getPlugin(); - if (loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.setPluginEnabled(PluginType.LOOP, false); - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_STOP")); - String reply = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " + - MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - }); - } else { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled)); - } - receivedSms.processed = true; - break; - case "ENABLE": - case "START": - loopPlugin = LoopPlugin.getPlugin(); - if (!loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.setPluginEnabled(PluginType.LOOP, true); - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled)); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_START")); - } else { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled)); - } - receivedSms.processed = true; - break; - case "STATUS": - loopPlugin = LoopPlugin.getPlugin(); - if (loopPlugin.isEnabled(PluginType.LOOP)) { - if (loopPlugin.isSuspended()) - reply = String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()); - else - reply = MainApp.gs(R.string.smscommunicator_loopisenabled); - } else { - reply = MainApp.gs(R.string.smscommunicator_loopisdisabled); - } - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - receivedSms.processed = true; - break; - case "RESUME": - LoopPlugin.getPlugin().suspendTo(0); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_RESUME")); - NSUpload.uploadOpenAPSOffline(0); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed)); - break; - case "SUSPEND": - int duration = 0; - if (splitted.length == 3) - duration = SafeParse.stringToInt(splitted[2]); - duration = Math.max(0, duration); - duration = Math.min(180, duration); - if (duration == 0) { - receivedSms.processed = true; - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration)); - return; - } else { - String passCode = generatePasscode(); - reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (result.success) { - LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000); - NSUpload.uploadOpenAPSOffline(anInteger * 60); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_SUSPENDED")); - String reply = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " + - MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - - } - }); - } - break; - default: - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - } - } - - private void processTREATMENTS(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("REFRESH")) { - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - sendSMS(new Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")); - receivedSms.processed = true; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processNSCLIENT(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("RESTART")) { - RxBus.INSTANCE.send(new EventNSClientRestart()); - sendSMS(new Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")); - receivedSms.processed = true; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - @SuppressWarnings("unused") - private void processPUMP(String[] splitted, Sms receivedSms) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() { - @Override - public void run() { - PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - if (result.success) { - if (pump != null) { - String reply = pump.shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } else { - String reply = MainApp.gs(R.string.readstatusfailed); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - receivedSms.processed = true; - } - - private void processPROFILE(String[] splitted, Sms receivedSms) { - // load profiles - ProfileInterface anInterface = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); - if (anInterface == null) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured)); - receivedSms.processed = true; - return; - } - ProfileStore store = anInterface.getProfile(); - if (store == null) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured)); - receivedSms.processed = true; - return; - } - final ArrayList list = store.getProfileList(); - - if (splitted[1].toUpperCase().equals("STATUS")) { - sendSMS(new Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().getProfileName())); - } else if (splitted[1].toUpperCase().equals("LIST")) { - if (list.isEmpty()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.invalidprofile)); - else { - String reply = ""; - for (int i = 0; i < list.size(); i++) { - if (i > 0) - reply += "\n"; - reply += (i + 1) + ". "; - reply += list.get(i); - } - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } else { - - int pindex = SafeParse.stringToInt(splitted[1]); - int percentage = 100; - if (splitted.length > 2) - percentage = SafeParse.stringToInt(splitted[2]); - - if (pindex > list.size()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (percentage == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (pindex == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - final Profile profile = store.getSpecificProfile((String) list.get(pindex - 1)); - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list.get(pindex - 1), percentage, passCode); - receivedSms.processed = true; - int finalPercentage = percentage; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction((String) list.get(pindex - 1), finalPercentage) { - @Override - public void run() { - ProfileFunctions.doProfileSwitch(store, (String) list.get(pindex - 1), 0, finalPercentage, 0); - sendSMS(new Sms(receivedSms.phoneNumber, R.string.profileswitchcreated)); - } - }); - } - } - } - receivedSms.processed = true; - } - - private void processBASAL(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (result.success) { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcanceled); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } else if (splitted[1].endsWith("%")) { - int tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%")); - int duration = 30; - if (splitted.length > 2) - duration = SafeParse.stringToInt(splitted[2]); - final Profile profile = ProfileFunctions.getInstance().getProfile(); - - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else if (tempBasalPct == 0 && !splitted[1].equals("0%")) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(new Constraint<>(tempBasalPct), profile).value(); - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasalPct, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(anInteger, secondInteger, true, profile, new Callback() { - @Override - public void run() { - if (result.success) { - String reply; - if (result.isPercent) - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration); - else - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } else { - Double tempBasal = SafeParse.stringToDouble(splitted[1]); - int duration = 30; - if (splitted.length > 2) - duration = SafeParse.stringToInt(splitted[2]); - final Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else if (tempBasal == 0 && !splitted[1].equals("0")) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(new Constraint<>(tempBasal), profile).value(); - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasal, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(aDouble, secondInteger, true, profile, new Callback() { - @Override - public void run() { - if (result.success) { - String reply; - if (result.isPercent) - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration); - else - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } - } - - private void processEXTENDED(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelExtended(new Callback() { - @Override - public void run() { - if (result.success) { - String reply = MainApp.gs(R.string.smscommunicator_extendedcanceled); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_extendedcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } else if (splitted.length != 3) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } else { - Double extended = SafeParse.stringToDouble(splitted[1]); - int duration = SafeParse.stringToInt(splitted[2]); - extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(new Constraint<>(extended)).value(); - if (extended == 0 || duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(extended, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().extendedBolus(aDouble, secondInteger, new Callback() { - @Override - public void run() { - if (result.success) { - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_extendedfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } - } - - - private void processBOLUS(String[] splitted, Sms receivedSms) { - Double bolus = SafeParse.stringToDouble(splitted[1]); - final boolean isMeal = splitted.length > 2 && splitted[2].equalsIgnoreCase("MEAL"); - bolus = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolus)).value(); - - if (splitted.length == 3 && !isMeal) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } else if (bolus > 0d) { - String passCode = generatePasscode(); - String reply = ""; - if (isMeal) { - reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode); - } else { - reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode); - } - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(bolus) { - @Override - public void run() { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.insulin = aDouble; - detailedBolusInfo.source = Source.USER; - ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - final boolean resultSuccess = result.success; - final double resultBolusDelivered = result.bolusDelivered; - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() { - @Override - public void run() { - PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - if (resultSuccess) { - String reply = ""; - if (isMeal) { - reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered); - } else { - reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered); - } - if (pump != null) - reply += "\n" + pump.shortStatus(true); - lastRemoteBolusTime = DateUtil.now(); - if (isMeal) { - Profile currentProfile = ProfileFunctions.getInstance().getProfile(); - int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); - eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration; - double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl); - eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl; - - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(eatingSoonTTDuration) - .reason(MainApp.gs(R.string.eatingsoon)) - .source(Source.USER) - .low(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits())) - .high(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - String tt = ""; - if (currentProfile.getUnits().equals(Constants.MMOL)) { - tt = DecimalFormatter.to1Decimal(eatingSoonTT); - } else - tt = DecimalFormatter.to0Decimal(eatingSoonTT); - - reply += String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration); - - - } - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_bolusfailed); - if (pump != null) - reply += "\n" + pump.shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processTARGET(String[] splitted, Sms receivedSms) { - boolean isMeal = splitted[1].equalsIgnoreCase("MEAL"); - boolean isActivity = splitted[1].equalsIgnoreCase("ACTIVITY"); - boolean isHypo = splitted[1].equalsIgnoreCase("HYPO"); - - if (isMeal || isActivity || isHypo) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - Profile currentProfile = ProfileFunctions.getInstance().getProfile(); - if (currentProfile != null) { - int keyDuration = 0; - Integer defaultTargetDuration = 0; - int keyTarget = 0; - double defaultTargetMMOL = 0d; - double defaultTargetMGDL = 0d; - - if (isMeal) { - keyDuration = R.string.key_eatingsoon_duration; - defaultTargetDuration = Constants.defaultEatingSoonTTDuration; - keyTarget = R.string.key_eatingsoon_target; - defaultTargetMMOL = Constants.defaultEatingSoonTTmmol; - defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl; - } else if (isActivity) { - keyDuration = R.string.key_activity_duration; - defaultTargetDuration = Constants.defaultActivityTTDuration; - keyTarget = R.string.key_activity_target; - defaultTargetMMOL = Constants.defaultActivityTTmmol; - defaultTargetMGDL = Constants.defaultActivityTTmgdl; - - } else if (isHypo) { - keyDuration = R.string.key_hypo_duration; - defaultTargetDuration = Constants.defaultHypoTTDuration; - keyTarget = R.string.key_hypo_target; - defaultTargetMMOL = Constants.defaultHypoTTmmol; - defaultTargetMGDL = Constants.defaultHypoTTmgdl; - } - - int ttDuration = SP.getInt(keyDuration, defaultTargetDuration); - ttDuration = ttDuration > 0 ? ttDuration : defaultTargetDuration; - double tt = SP.getDouble(keyTarget, currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL); - tt = tt > 0 ? tt : currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL; - - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(ttDuration) - .reason(MainApp.gs(R.string.eatingsoon)) - .source(Source.USER) - .low(Profile.toMgdl(tt, currentProfile.getUnits())) - .high(Profile.toMgdl(tt, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - String ttString = ""; - if (currentProfile.getUnits().equals(Constants.MMOL)) - ttString = DecimalFormatter.to1Decimal(tt); - else - ttString = DecimalFormatter.to0Decimal(tt); - - String reply = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); - } - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processSMS(String[] splitted, Sms receivedSms) { - boolean isStop = splitted[1].equalsIgnoreCase("STOP") - || splitted[1].equalsIgnoreCase("DISABLE"); - - if (isStop) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); - String reply = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processCAL(String[] splitted, Sms receivedSms) { - Double cal = SafeParse.stringToDouble(splitted[1]); - if (cal > 0d) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(cal) { - @Override - public void run() { - boolean result = XdripCalibrations.sendIntent(aDouble); - if (result) - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed)); - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - public boolean sendNotificationToAllNumbers(String text) { - boolean result = true; - for (int i = 0; i < allowedNumbers.size(); i++) { - Sms sms = new Sms(allowedNumbers.get(i), text); - result = result && sendSMS(sms); - } - return result; - } - - private void sendSMSToAllNumbers(Sms sms) { - for (String number : allowedNumbers) { - sms.phoneNumber = number; - sendSMS(sms); - } - } - - boolean sendSMS(Sms sms) { - SmsManager smsManager = SmsManager.getDefault(); - sms.text = stripAccents(sms.text); - - try { - if (L.isEnabled(L.SMS)) - log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text); - if (sms.text.getBytes().length <= 140) - smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null); - else { - ArrayList parts = smsManager.divideMessage(sms.text); - smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, - null, null); - } - - messages.add(sms); - } catch (IllegalArgumentException e) { - if (e.getMessage().equals("Invalid message body")) { - Notification notification = new Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } else { - Notification notification = new Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } - } catch (java.lang.SecurityException e) { - Notification notification = new Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - return true; - } - - private String generatePasscode() { - int startChar1 = 'A'; // on iphone 1st char is uppercase :) - String passCode = Character.toString((char) (startChar1 + Math.random() * ('z' - 'a' + 1))); - int startChar2 = Math.random() > 0.5 ? 'a' : 'A'; - passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1))); - int startChar3 = Math.random() > 0.5 ? 'a' : 'A'; - passCode += Character.toString((char) (startChar3 + Math.random() * ('z' - 'a' + 1))); - passCode = passCode.replace('l', 'k').replace('I', 'J'); - return passCode; - } - - private static String stripAccents(String s) { - s = Normalizer.normalize(s, Normalizer.Form.NFD); - s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""); - return s; - } - - public static boolean areMoreNumbers(String allowednumbers) { - int countNumbers = 0; - String[] substrings = allowednumbers.split(";"); - - for (String number : substrings) { - String cleaned = number.replaceAll("\\s+", ""); - if (cleaned.length() < 4) continue; - if (cleaned.substring(0, 1).compareTo("+") != 0) continue; - cleaned = cleaned.replace("+", ""); - if (!cleaned.matches("[0-9]+")) continue; - countNumbers++; - } - return countNumbers > 1; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt new file mode 100644 index 0000000000..037f17d0bb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -0,0 +1,812 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import android.content.Intent +import android.preference.EditTextPreference +import android.preference.Preference +import android.preference.Preference.OnPreferenceChangeListener +import android.preference.PreferenceFragment +import android.telephony.SmsManager +import android.telephony.SmsMessage +import android.text.TextUtils +import com.andreabaccega.widget.ValidatingEditTextPreference +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.DatabaseHelper +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.bus.RxBus.send +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.* +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import org.apache.commons.lang3.StringUtils +import org.slf4j.LoggerFactory +import java.text.Normalizer +import java.util.* + +/** + * Created by mike on 05.08.2016. + */ +class SmsCommunicatorPlugin internal constructor() : PluginBase(PluginDescription() + .mainType(PluginType.GENERAL) + .fragmentClass(SmsCommunicatorFragment::class.java.name) + .pluginName(R.string.smscommunicator) + .shortName(R.string.smscommunicator_shortname) + .preferencesId(R.xml.pref_smscommunicator) + .description(R.string.description_sms_communicator) +) { + private val disposable = CompositeDisposable() + var allowedNumbers: MutableList = ArrayList() + var messageToConfirm: AuthRequest? = null + var lastRemoteBolusTime: Long = 0 + var messages = ArrayList() + + override fun onStart() { + super.onStart() + disposable.add(toObservable(EventPreferenceChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventPreferenceChange? -> processSettings(event) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) } + ) + } + + override fun onStop() { + disposable.clear() + super.onStop() + } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragment) { + super.preprocessPreferences(preferenceFragment) + val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference + val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference + if (distance != null && allowedNumbers != null) { + if (!areMoreNumbers(allowedNumbers.text)) { + distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) + distance.isEnabled = false + } else { + distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + distance.isEnabled = true + } + allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { preference: Preference?, newValue: Any -> + if (!areMoreNumbers(newValue as String)) { + distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString() + distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) + distance.isEnabled = false + } else { + distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + distance.isEnabled = true + } + true + } + } + } + + override fun updatePreferenceSummary(pref: Preference) { + super.updatePreferenceSummary(pref) + if (pref is EditTextPreference) { + val editTextPref = pref + if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.text == null || TextUtils.isEmpty(editTextPref.text.trim { it <= ' ' }))) { + pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)) + } + } + } + + private fun processSettings(ev: EventPreferenceChange?) { + if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) { + val settings = SP.getString(R.string.key_smscommunicator_allowednumbers, "") + allowedNumbers.clear() + val substrings = settings.split(";").toTypedArray() + for (number in substrings) { + val cleaned = number.replace("\\s+".toRegex(), "") + allowedNumbers.add(cleaned) + log.debug("Found allowed number: $cleaned") + } + } + } + + fun isCommand(command: String, number: String): Boolean { + when (command.toUpperCase(Locale.getDefault())) { + "BG", "LOOP", "TREATMENTS", "NSCLIENT", "PUMP", "BASAL", "BOLUS", "EXTENDED", "CAL", "PROFILE", "TARGET", "SMS" -> return true + } + return messageToConfirm?.requester?.phoneNumber == number + } + + fun isAllowedNumber(number: String): Boolean { + for (num in allowedNumbers) { + if (num == number) return true + } + return false + } + + fun handleNewData(intent: Intent) { + val bundle = intent.extras ?: return + val pdus = bundle["pdus"] as Array<*> + for (pdu in pdus) { + val message = SmsMessage.createFromPdu(pdu as ByteArray) + processSms(Sms(message)) + } + } + + fun processSms(receivedSms: Sms) { + if (!isEnabled(PluginType.GENERAL)) { + log.debug("Ignoring SMS. Plugin disabled.") + return + } + if (!isAllowedNumber(receivedSms.phoneNumber)) { + log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed") + receivedSms.ignored = true + messages.add(receivedSms) + send(EventSmsCommunicatorUpdateGui()) + return + } + val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return + messages.add(receivedSms) + log.debug(receivedSms.toString()) + val splitted = receivedSms.text.split(Regex("\\s+")).toTypedArray() + val remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) + if (splitted.isNotEmpty() && isCommand(splitted[0].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { + when (splitted[0].toUpperCase(Locale.getDefault())) { + "BG" -> + if (splitted.size == 1) processBG(receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "LOOP" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processLOOP(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "TREATMENTS" -> + if (splitted.size == 2) processTREATMENTS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "NSCLIENT" -> + if (splitted.size == 2) processNSCLIENT(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "PUMP" -> + if (splitted.size == 1) processPUMP(receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "PROFILE" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processPROFILE(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "BASAL" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processBASAL(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "EXTENDED" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processEXTENDED(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "BOLUS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)) + else if (splitted.size == 2 && pump.isSuspended) sendSMS(Sms(receivedSms.phoneNumber, R.string.pumpsuspended)) + else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "CAL" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processCAL(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "TARGET" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processTARGET(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "SMS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processSMS(splitted, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else -> + if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { + messageToConfirm?.action(splitted[0]) + messageToConfirm = null + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)) + } + } + send(EventSmsCommunicatorUpdateGui()) + } + + private fun processBG(receivedSms: Sms) { + val actualBG = DatabaseHelper.actualBg() + val lastBG = DatabaseHelper.lastBg() + var reply = "" + val units = ProfileFunctions.getInstance().profileUnits + if (actualBG != null) { + reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", " + } else if (lastBG != null) { + val agoMsec = System.currentTimeMillis() - lastBG.date + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", " + } + val glucoseStatus = GlucoseStatus.getGlucoseStatusData() + if (glucoseStatus != null) reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", " + TreatmentsPlugin.getPlugin().updateTotalIOBTreatments() + val bolusIob = TreatmentsPlugin.getPlugin().lastCalculationTreatments.round() + TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals() + val basalIob = TreatmentsPlugin.getPlugin().lastCalculationTempBasals.round() + val cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB") + reply += (MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" + + MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " + + MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), " + + MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString()) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + receivedSms.processed = true + } + + private fun processLOOP(splitted: Array, receivedSms: Sms) { + when (splitted[1].toUpperCase(Locale.getDefault())) { + "DISABLE", "STOP" -> { + val loopPlugin = LoopPlugin.getPlugin() + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.setPluginEnabled(PluginType.LOOP, false) + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + send(EventRefreshOverview("SMS_LOOP_STOP")) + val replyText = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " + + MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else { + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled)) + } + receivedSms.processed = true + } + "ENABLE", "START" -> { + val loopPlugin = LoopPlugin.getPlugin() + if (!loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.setPluginEnabled(PluginType.LOOP, true) + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled)) + send(EventRefreshOverview("SMS_LOOP_START")) + } else { + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled)) + } + receivedSms.processed = true + } + "STATUS" -> { + val loopPlugin = LoopPlugin.getPlugin() + val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { + if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) else MainApp.gs(R.string.smscommunicator_loopisenabled) + } else { + MainApp.gs(R.string.smscommunicator_loopisdisabled) + } + sendSMS(Sms(receivedSms.phoneNumber, reply)) + receivedSms.processed = true + } + "RESUME" -> { + LoopPlugin.getPlugin().suspendTo(0) + send(EventRefreshOverview("SMS_LOOP_RESUME")) + NSUpload.uploadOpenAPSOffline(0.0) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed)) + } + "SUSPEND" -> { + var duration = 0 + if (splitted.size == 3) duration = SafeParse.stringToInt(splitted[2]) + duration = Math.max(0, duration) + duration = Math.min(180, duration) + if (duration == 0) { + receivedSms.processed = true + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration)) + return + } else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (result.success) { + LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000) + NSUpload.uploadOpenAPSOffline(anInteger * 60.toDouble()) + send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) + val replyText = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " + + MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + else -> sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + } + + private fun processTREATMENTS(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "REFRESH") { + TreatmentsPlugin.getPlugin().service.resetTreatments() + send(EventNSClientRestart()) + sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")) + receivedSms.processed = true + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processNSCLIENT(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "RESTART") { + send(EventNSClientRestart()) + sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")) + receivedSms.processed = true + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processPUMP(receivedSms: Sms) { + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { + override fun run() { + val pump = ConfigBuilderPlugin.getPlugin().activePump + if (result.success) { + if (pump != null) { + val reply = pump.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } else { + val reply = MainApp.gs(R.string.readstatusfailed) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } + }) + receivedSms.processed = true + } + + private fun processPROFILE(splitted: Array, receivedSms: Sms) { // load profiles + val anInterface = ConfigBuilderPlugin.getPlugin().activeProfileInterface + if (anInterface == null) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured)) + receivedSms.processed = true + return + } + val store = anInterface.profile + if (store == null) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured)) + receivedSms.processed = true + return + } + val list = store.profileList + if (splitted[1].toUpperCase(Locale.getDefault()) == "STATUS") { + sendSMS(Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().profileName)) + } else if (splitted[1].toUpperCase(Locale.getDefault()) == "LIST") { + if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile)) else { + var reply = "" + for (i in list.indices) { + if (i > 0) reply += "\n" + reply += (i + 1).toString() + ". " + reply += list[i] + } + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } else { + val pindex = SafeParse.stringToInt(splitted[1]) + var percentage = 100 + if (splitted.size > 2) percentage = SafeParse.stringToInt(splitted[2]) + if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + val profile = store.getSpecificProfile(list[pindex - 1] as String) + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode) + receivedSms.processed = true + val finalPercentage = percentage + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) { + override fun run() { + ProfileFunctions.doProfileSwitch(store, list[pindex - 1] as String, 0, finalPercentage, 0) + sendSMS(Sms(receivedSms.phoneNumber, R.string.profileswitchcreated)) + } + }) + } + } + } + receivedSms.processed = true + } + + private fun processBASAL(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (result.success) { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcanceled) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } else if (splitted[1].endsWith("%")) { + var tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%")) + var duration = 30 + if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) + val profile = ProfileFunctions.getInstance().profile + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value() + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger, secondInteger, true, profile, object : Callback() { + override fun run() { + if (result.success) { + var replyText: String + replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } else { + var tempBasal = SafeParse.stringToDouble(splitted[1]) + var duration = 30 + if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) + val profile = ProfileFunctions.getInstance().profile + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(tempBasal), profile).value() + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble, secondInteger, true, profile, object : Callback() { + override fun run() { + if (result.success) { + var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + } + + private fun processEXTENDED(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelExtended(object : Callback() { + override fun run() { + if (result.success) { + var replyText = MainApp.gs(R.string.smscommunicator_extendedcanceled) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_extendedcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } else if (splitted.size != 3) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else { + var extended = SafeParse.stringToDouble(splitted[1]) + val duration = SafeParse.stringToInt(splitted[2]) + extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(extended)).value() + if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(extended, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble, secondInteger, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_extendedfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + } + + private fun processBOLUS(splitted: Array, receivedSms: Sms) { + var bolus = SafeParse.stringToDouble(splitted[1]) + val isMeal = splitted.size > 2 && splitted[2].equals("MEAL", ignoreCase = true) + bolus = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(bolus)).value() + if (splitted.size == 3 && !isMeal) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else if (bolus > 0.0) { + val passCode = generatePasscode() + val reply = if (isMeal) { + String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode) + } else { + String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) + } + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) { + override fun run() { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.insulin = aDouble + detailedBolusInfo.source = Source.USER + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + val resultSuccess = result.success + val resultBolusDelivered = result.bolusDelivered + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { + override fun run() { + if (resultSuccess) { + var replyText = if (isMeal) { + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered) + } else { + String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered) + } + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + lastRemoteBolusTime = DateUtil.now() + if (isMeal) { + ProfileFunctions.getInstance().profile?.let { currentProfile -> + var eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration) + eatingSoonTTDuration = if (eatingSoonTTDuration > 0) eatingSoonTTDuration else Constants.defaultEatingSoonTTDuration + var eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl) + eatingSoonTT = if (eatingSoonTT > 0) eatingSoonTT else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(eatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(eatingSoonTT, currentProfile.units)) + .high(Profile.toMgdl(eatingSoonTT, currentProfile.units)) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val tt = if (currentProfile.units == Constants.MMOL) { + DecimalFormatter.to1Decimal(eatingSoonTT) + } else DecimalFormatter.to0Decimal(eatingSoonTT) + replyText += "\n" + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration) + } + } + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_bolusfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processTARGET(splitted: Array, receivedSms: Sms) { + val isMeal = splitted[1].equals("MEAL", ignoreCase = true) + val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true) + val isHypo = splitted[1].equals("HYPO", ignoreCase = true) + if (isMeal || isActivity || isHypo) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + val currentProfile = ProfileFunctions.getInstance().profile + if (currentProfile != null) { + var keyDuration = 0 + var defaultTargetDuration = 0 + var keyTarget = 0 + var defaultTargetMMOL = 0.0 + var defaultTargetMGDL = 0.0 + if (isMeal) { + keyDuration = R.string.key_eatingsoon_duration + defaultTargetDuration = Constants.defaultEatingSoonTTDuration + keyTarget = R.string.key_eatingsoon_target + defaultTargetMMOL = Constants.defaultEatingSoonTTmmol + defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl + } else if (isActivity) { + keyDuration = R.string.key_activity_duration + defaultTargetDuration = Constants.defaultActivityTTDuration + keyTarget = R.string.key_activity_target + defaultTargetMMOL = Constants.defaultActivityTTmmol + defaultTargetMGDL = Constants.defaultActivityTTmgdl + } else if (isHypo) { + keyDuration = R.string.key_hypo_duration + defaultTargetDuration = Constants.defaultHypoTTDuration + keyTarget = R.string.key_hypo_target + defaultTargetMMOL = Constants.defaultHypoTTmmol + defaultTargetMGDL = Constants.defaultHypoTTmgdl + } + var ttDuration = SP.getInt(keyDuration, defaultTargetDuration) + ttDuration = if (ttDuration > 0) ttDuration else defaultTargetDuration + var tt = SP.getDouble(keyTarget, if (currentProfile.units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL) + tt = if (tt > 0) tt else if (currentProfile.units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(ttDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(tt, currentProfile.units)) + .high(Profile.toMgdl(tt, currentProfile.units)) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val ttString = if (currentProfile.units == Constants.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)) + } + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processSMS(splitted: Array, receivedSms: Sms) { + val isStop = (splitted[1].equals("STOP", ignoreCase = true) + || splitted[1].equals("DISABLE", ignoreCase = true)) + if (isStop) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processCAL(splitted: Array, receivedSms: Sms) { + val cal = SafeParse.stringToDouble(splitted[1]) + if (cal > 0.0) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(cal) { + override fun run() { + val result = XdripCalibrations.sendIntent(aDouble) + if (result) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)) else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed)) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + fun sendNotificationToAllNumbers(text: String?): Boolean { + var result = true + for (i in allowedNumbers.indices) { + val sms = Sms(allowedNumbers[i], text) + result = result && sendSMS(sms) + } + return result + } + + private fun sendSMSToAllNumbers(sms: Sms) { + for (number in allowedNumbers) { + sms.phoneNumber = number + sendSMS(sms) + } + } + + fun sendSMS(sms: Sms): Boolean { + val smsManager = SmsManager.getDefault() + sms.text = stripAccents(sms.text) + try { + if (L.isEnabled(L.SMS)) log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text) + if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) else { + val parts = smsManager.divideMessage(sms.text) + smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, + null, null) + } + messages.add(sms) + } catch (e: IllegalArgumentException) { + return if (e.message == "Invalid message body") { + val notification = Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL) + send(EventNewNotification(notification)) + false + } else { + val notification = Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL) + send(EventNewNotification(notification)) + false + } + } catch (e: SecurityException) { + val notification = Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL) + send(EventNewNotification(notification)) + return false + } + send(EventSmsCommunicatorUpdateGui()) + return true + } + + private fun generatePasscode(): String { + val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :) + var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar()) + val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() + passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar()) + val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() + passCode += Character.toString((startChar3 + Math.random() * ('z' - 'a' + 1)).toChar()) + passCode = passCode.replace('l', 'k').replace('I', 'J') + return passCode + } + + companion object { + private val log = LoggerFactory.getLogger(L.SMS) + private var smsCommunicatorPlugin: SmsCommunicatorPlugin? = null + @JvmStatic + val plugin: SmsCommunicatorPlugin? + get() { + if (smsCommunicatorPlugin == null) { + smsCommunicatorPlugin = SmsCommunicatorPlugin() + } + return smsCommunicatorPlugin + } + + private fun stripAccents(str: String): String { + var s = str + s = Normalizer.normalize(s, Normalizer.Form.NFD) + s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "") + return s + } + + fun areMoreNumbers(allowednumbers: String): Boolean { + var countNumbers = 0 + val substrings = allowednumbers.split(";").toTypedArray() + for (number in substrings) { + var cleaned = number.replace(Regex("\\s+"), "") + if (cleaned.length < 4) continue + if (cleaned.substring(0, 1).compareTo("+") != 0) continue + cleaned = cleaned.replace("+", "") + if (!cleaned.matches(Regex("[0-9]+"))) continue + countNumbers++ + } + return countNumbers > 1 + } + } + + init { + processSettings(null) + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f37cc9a85..2fba2a59fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -307,7 +307,7 @@ Bolus %1$.2fU delivered successfully Meal Bolus %1$.2fU delivered successfully Target %1$s for %2$d minutes - Target %1$s for %2$d minutes set succesfully + Target %1$s for %2$d minutes set successfully Delivering %1$.2fU Allow remote commands via SMS Finger diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index a36c989b11..bd01e6b89b 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -155,6 +155,9 @@ public class AAPSMocker { when(MainApp.gs(R.string.pumpNotInitialized)).thenReturn("Pump not initialized!"); when(MainApp.gs(R.string.increasingmaxbasal)).thenReturn("Increasing max basal value because setting is lower than your max basal in profile"); when(MainApp.gs(R.string.overview_bolusprogress_delivered)).thenReturn("Delivered"); + when(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode)).thenReturn("To deliver meal bolus %1$.2fU reply with code %2$s"); + when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered)).thenReturn("Meal Bolus %1$.2fU delivered successfully"); + when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt)).thenReturn("Target %1$s for %2$d minutes"); } public static MainApp mockMainApp() { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java index dd41f9873a..7b53e214a8 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java @@ -8,8 +8,6 @@ import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import java.util.Date; - import info.AAPSMocker; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -28,9 +26,9 @@ import static org.powermock.api.mockito.PowerMockito.when; @PrepareForTest({SmsCommunicatorPlugin.class, L.class, SP.class, MainApp.class, DateUtil.class}) public class AuthRequestTest { - SmsCommunicatorPlugin smsCommunicatorPlugin; - Sms sentSms; - boolean actionCalled = false; + private SmsCommunicatorPlugin smsCommunicatorPlugin; + private Sms sentSms; + private boolean actionCalled = false; @Test public void doTests() { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 6e83032504..77324d9627 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -25,7 +25,6 @@ import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; @@ -67,27 +66,29 @@ public class SmsCommunicatorPluginTest { private boolean hasBeenRun = false; + private VirtualPumpPlugin virtualPumpPlugin; + @Test public void processSettingsTest() { // called from constructor - Assert.assertEquals("1234", smsCommunicatorPlugin.allowedNumbers.get(0)); - Assert.assertEquals("5678", smsCommunicatorPlugin.allowedNumbers.get(1)); - Assert.assertEquals(2, smsCommunicatorPlugin.allowedNumbers.size()); + Assert.assertEquals("1234", smsCommunicatorPlugin.getAllowedNumbers().get(0)); + Assert.assertEquals("5678", smsCommunicatorPlugin.getAllowedNumbers().get(1)); + Assert.assertEquals(2, smsCommunicatorPlugin.getAllowedNumbers().size()); } @Test public void isCommandTest() { Assert.assertTrue(smsCommunicatorPlugin.isCommand("BOLUS", "")); - smsCommunicatorPlugin.messageToConfirm = null; + smsCommunicatorPlugin.setMessageToConfirm(null); Assert.assertFalse(smsCommunicatorPlugin.isCommand("BLB", "")); - smsCommunicatorPlugin.messageToConfirm = new AuthRequest(smsCommunicatorPlugin, new Sms("1234", "ddd"), "RequestText", "ccode", new SmsAction() { + smsCommunicatorPlugin.setMessageToConfirm(new AuthRequest(smsCommunicatorPlugin, new Sms("1234", "ddd"), "RequestText", "ccode", new SmsAction() { @Override public void run() { } - }); + })); Assert.assertTrue(smsCommunicatorPlugin.isCommand("BLB", "1234")); Assert.assertFalse(smsCommunicatorPlugin.isCommand("BLB", "2345")); - smsCommunicatorPlugin.messageToConfirm = null; + smsCommunicatorPlugin.setMessageToConfirm(null); } @Test @@ -101,82 +102,82 @@ public class SmsCommunicatorPluginTest { Sms sms; // SMS from not allowed number should be ignored - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("12", "aText"); smsCommunicatorPlugin.processSms(sms); Assert.assertTrue(sms.ignored); - Assert.assertEquals("aText", smsCommunicatorPlugin.messages.get(0).text); + Assert.assertEquals("aText", smsCommunicatorPlugin.getMessages().get(0).text); //UNKNOWN - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "UNKNOWN"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("UNKNOWN", smsCommunicatorPlugin.messages.get(0).text); + Assert.assertEquals("UNKNOWN", smsCommunicatorPlugin.getMessages().get(0).text); //BG - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BG"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BG", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("IOB:")); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("Last BG: 100")); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("COB: 10(2)g")); + Assert.assertEquals("BG", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("IOB:")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Last BG: 100")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("COB: 10(2)g")); // LOOP : test remote control disabled when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("Remote command is not allowed")); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Remote command is not allowed")); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //LOOP STATUS : disabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP STATUS : suspended when(loopPlugin.minutesToEndOfSuspend()).thenReturn(10); when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(true); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP STATUS : enabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP : wrong format when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP DISABLE : already disabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP DISABLE"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP DISABLE : from enabled hasBeenRun = false; @@ -185,22 +186,22 @@ public class SmsCommunicatorPluginTest { hasBeenRun = true; return null; }).when(loopPlugin).setPluginEnabled(PluginType.LOOP, false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP DISABLE"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.getMessages().get(1).text); Assert.assertTrue(hasBeenRun); //LOOP ENABLE : already enabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP ENABLE"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP ENABLE : from disabled hasBeenRun = false; @@ -209,142 +210,142 @@ public class SmsCommunicatorPluginTest { hasBeenRun = true; return null; }).when(loopPlugin).setPluginEnabled(PluginType.LOOP, true); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP ENABLE"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.getMessages().get(1).text); Assert.assertTrue(hasBeenRun); //LOOP RESUME : already enabled - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP RESUME"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP RESUME", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Loop resumed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP RESUME", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Loop resumed", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP SUSPEND 1 2: wrong format - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 1 2"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 1 2", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP SUSPEND 1 2", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP SUSPEND 0 : wrong duration - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 0"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP SUSPEND 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.getMessages().get(1).text); //LOOP SUSPEND 100 : suspend for 100 min + correct answer - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 100"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 100", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To suspend loop for 100 minutes reply with code ")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("LOOP SUSPEND 100", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To suspend loop for 100 minutes reply with code ")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Loop suspended Temp basal canceled", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Loop suspended Temp basal canceled", smsCommunicatorPlugin.getMessages().get(3).text); //LOOP SUSPEND 200 : limit to 180 min + wrong answer - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 200"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 200", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To suspend loop for 180 minutes reply with code ")); - passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("LOOP SUSPEND 200", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To suspend loop for 180 minutes reply with code ")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; // ignore from other number smsCommunicatorPlugin.processSms(new Sms("5678", passCode)); smsCommunicatorPlugin.processSms(new Sms("1234", "XXXX")); - Assert.assertEquals("XXXX", smsCommunicatorPlugin.messages.get(3).text); - Assert.assertEquals("Wrong code. Command cancelled.", smsCommunicatorPlugin.messages.get(4).text); + Assert.assertEquals("XXXX", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals("Wrong code. Command cancelled.", smsCommunicatorPlugin.getMessages().get(4).text); //then correct code should not work smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(5).text); - Assert.assertEquals(6, smsCommunicatorPlugin.messages.size()); // processed as common message + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(5).text); + Assert.assertEquals(6, smsCommunicatorPlugin.getMessages().size()); // processed as common message //LOOP BLABLA - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP BLABLA"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP BLABLA", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("LOOP BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //TREATMENTS REFRESH when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS REFRESH"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS REFRESH", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("TREATMENTS REFRESH")); + Assert.assertEquals("TREATMENTS REFRESH", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("TREATMENTS REFRESH")); //TREATMENTS BLA BLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS BLA BLA"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS BLA BLA", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("TREATMENTS BLA BLA", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //TREATMENTS BLABLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS BLABLA"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS BLABLA", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("TREATMENTS BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //NSCLIENT RESTART when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT RESTART"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT RESTART", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("NSCLIENT RESTART")); + Assert.assertEquals("NSCLIENT RESTART", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("NSCLIENT RESTART")); //NSCLIENT BLA BLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT BLA BLA"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT BLA BLA", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("NSCLIENT BLA BLA", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //NSCLIENT BLABLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); when(loopPlugin.isSuspended()).thenReturn(false); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT BLABLA"); smsCommunicatorPlugin.processSms(sms); Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT BLABLA", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("NSCLIENT BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //PUMP - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PUMP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PUMP", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PUMP", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.getMessages().get(1).text); } @@ -353,92 +354,92 @@ public class SmsCommunicatorPluginTest { Sms sms; //PROFILE - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //PROFILE - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE LIST (no profile interface) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Not configured", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).text); ProfileInterface profileInterface = mock(SimpleProfilePlugin.class); when(ConfigBuilderPlugin.getPlugin().getActiveProfileInterface()).thenReturn(profileInterface); //PROFILE LIST (no profile defined) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Not configured", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).text); when(profileInterface.getProfile()).thenReturn(AAPSMocker.getValidProfileStore()); //PROFILE STATUS - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE STATUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals(AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE STATUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals(AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE LIST - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("1. " + AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("1. " + AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE 2 (non existing) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 2"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 2", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE 2", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE 1 0(wrong percentage) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE 1 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE 0(wrong index) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("PROFILE 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //PROFILE 1(OK) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To switch profile to someProfile 100% reply with code")); + Assert.assertEquals("PROFILE 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To switch profile to someProfile 100% reply with code")); //PROFILE 1 90(OK) - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1 90"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1 90", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To switch profile to someProfile 90% reply with code")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("PROFILE 1 90", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To switch profile to someProfile 90% reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Profile switch created", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Profile switch created", smsCommunicatorPlugin.getMessages().get(3).text); } @Test @@ -446,85 +447,85 @@ public class SmsCommunicatorPluginTest { Sms sms; //BASAL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //BASAL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //BASAL CANCEL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL CANCEL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL CANCEL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To stop temp basal reply with code")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("BASAL CANCEL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To stop temp basal reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Temp basal canceled\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Temp basal canceled")); //BASAL a% - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL a%"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL a%", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL a%", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //BASAL 10% 0 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 10% 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 10% 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL 10% 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyBasalPercentConstraints(any(), any())).thenReturn(new Constraint<>(20)); //BASAL 20% 20 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 20% 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 20% 20", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To start basal 20% for 20 min reply with code")); - passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("BASAL 20% 20", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start basal 20% for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Temp basal 20% for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Temp basal 20% for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); //BASAL a - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL a", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL a", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //BASAL 1 0 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 1 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BASAL 1 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyBasalConstraints(any(), any())).thenReturn(new Constraint<>(1d)); //BASAL 1 20 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 1 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 1 20", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To start basal 1.00U/h for 20 min reply with code")); - passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("BASAL 1 20", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start basal 1.00U/h for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Temp basal 1.00U/h for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Temp basal 1.00U/h for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); } @@ -533,58 +534,58 @@ public class SmsCommunicatorPluginTest { Sms sms; //EXTENDED - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //EXTENDED - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //EXTENDED CANCEL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED CANCEL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED CANCEL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To stop extended bolus reply with code")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("EXTENDED CANCEL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To stop extended bolus reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Extended bolus canceled\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Extended bolus canceled")); //EXTENDED a% - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED a%"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED a%", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("EXTENDED a%", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyExtendedBolusConstraints(any())).thenReturn(new Constraint<>(1d)); //EXTENDED 1 0 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED 1 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("EXTENDED 1 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //EXTENDED 1 20 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED 1 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED 1 20", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To start extended bolus 1.00U for 20 min reply with code")); - passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("EXTENDED 1 20", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start extended bolus 1.00U for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Extended bolus 1.00U for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Extended bolus 1.00U for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); } @Test @@ -592,104 +593,91 @@ public class SmsCommunicatorPluginTest { Sms sms; //BOLUS - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //BOLUS - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyBolusConstraints(any())).thenReturn(new Constraint<>(1d)); when(DateUtil.now()).thenReturn(1000L); //BOLUS 1 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote bolus not available. Try again later.", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote bolus not available. Try again later.", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyBolusConstraints(any())).thenReturn(new Constraint<>(0d)); when(DateUtil.now()).thenReturn(Constants.remoteBolusMinDistance + 1002L); //BOLUS 0 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //BOLUS a - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS a", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS a", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(MainApp.getConstraintChecker().applyExtendedBolusConstraints(any())).thenReturn(new Constraint<>(1d)); when(MainApp.getConstraintChecker().applyBolusConstraints(any())).thenReturn(new Constraint<>(1d)); //BOLUS 1 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To deliver bolus 1.00U reply with code")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To deliver bolus 1.00U reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Bolus 1.00U delivered successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Bolus 1.00U delivered successfully")); //BOLUS 1 (Suspended pump) - smsCommunicatorPlugin.lastRemoteBolusTime = 0; - PumpInterface pump = mock(VirtualPumpPlugin.class); - when(ConfigBuilderPlugin.getPlugin().getActivePump()).thenReturn(pump); - when(pump.isSuspended()).thenReturn(true); - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setLastRemoteBolusTime(0); + when(virtualPumpPlugin.isSuspended()).thenReturn(true); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.messages.get(1).text); - when(pump.isSuspended()).thenReturn(false); + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.getMessages().get(1).text); + when(virtualPumpPlugin.isSuspended()).thenReturn(false); //BOLUS 1 a - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1 a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1 a", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("BOLUS 1 a", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //BOLUS 1 MEAL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1 MEAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To deliver meal bolus 1.00U reply with code")); - passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To deliver meal bolus 1.00U reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Meal Bolus 1.00U delivered successfully\nVirtual Pump", smsCommunicatorPlugin.messages.get(3).text); - - //BOLUS 1 MEAL (Suspended pump) - smsCommunicatorPlugin.lastRemoteBolusTime = 0; - when(ConfigBuilderPlugin.getPlugin().getActivePump()).thenReturn(pump); - when(pump.isSuspended()).thenReturn(true); - smsCommunicatorPlugin.messages = new ArrayList<>(); - sms = new Sms("1234", "BOLUS 1 MEAL"); - smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.messages.get(1).text); - when(pump.isSuspended()).thenReturn(false); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Meal Bolus 1.00U delivered successfully\nVirtual Pump\nTarget 5.0 for 45 minutes", smsCommunicatorPlugin.getMessages().get(3).text); } @Test @@ -697,47 +685,47 @@ public class SmsCommunicatorPluginTest { Sms sms; //CAL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //CAL - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); //CAL 0 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL 0", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("CAL 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); when(XdripCalibrations.sendIntent(any())).thenReturn(true); //CAL 1 - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL 1", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.messages.get(1).text.contains("To send calibration 1.00 reply with code")); - String passCode = smsCommunicatorPlugin.messageToConfirm.confirmCode; + Assert.assertEquals("CAL 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To send calibration 1.00 reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.messages.get(2).text); - Assert.assertEquals("Calibration sent. Receiving must be enabled in xDrip.", smsCommunicatorPlugin.messages.get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertEquals("Calibration sent. Receiving must be enabled in xDrip.", smsCommunicatorPlugin.getMessages().get(3).text); } @Test public void sendNotificationToAllNumbers() { - smsCommunicatorPlugin.messages = new ArrayList<>(); + smsCommunicatorPlugin.setMessages(new ArrayList<>()); smsCommunicatorPlugin.sendNotificationToAllNumbers("abc"); - Assert.assertEquals("abc", smsCommunicatorPlugin.messages.get(0).text); - Assert.assertEquals("abc", smsCommunicatorPlugin.messages.get(1).text); + Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(1).text); } @Before @@ -826,8 +814,10 @@ public class SmsCommunicatorPluginTest { return null; }).when(AAPSMocker.queue).extendedBolus(anyDouble(), anyInt(), any(Callback.class)); - VirtualPumpPlugin virtualPumpPlugin = VirtualPumpPlugin.getPlugin(); + virtualPumpPlugin = mock(VirtualPumpPlugin.class); when(ConfigBuilderPlugin.getPlugin().getActivePump()).thenReturn(virtualPumpPlugin); + when(virtualPumpPlugin.shortStatus(anyBoolean())).thenReturn("Virtual Pump"); + when(virtualPumpPlugin.isSuspended()).thenReturn(false); } } From a62750c3a32dd5b6692d38fc9a21aa16346af335 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 00:33:13 +0100 Subject: [PATCH 32/56] SmsCommunicatorFragment -> kt --- .../info/nightscout/androidaps/MainApp.java | 2 +- .../activities/PreferencesActivity.java | 2 +- .../automation/actions/ActionSendSMS.java | 2 +- .../SmsCommunicatorFragment.java | 83 ------------------- .../SmsCommunicatorFragment.kt | 70 ++++++++++++++++ .../smsCommunicator/SmsCommunicatorPlugin.kt | 60 ++++++-------- .../queue/commands/CommandSetProfile.java | 2 +- .../androidaps/services/DataService.java | 2 +- .../SmsCommunicatorPluginTest.java | 2 +- 9 files changed, 100 insertions(+), 125 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 379afabddf..0cf275145d 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -204,7 +204,7 @@ public class MainApp extends Application { pluginsList.add(SourcePoctechPlugin.getPlugin()); pluginsList.add(SourceTomatoPlugin.getPlugin()); pluginsList.add(SourceEversensePlugin.getPlugin()); - if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.INSTANCE); pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(WearPlugin.initPlugin(this)); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index f29d2af429..0e93a47151 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -184,7 +184,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL); - addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL); + addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResource(R.xml.pref_others); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java index f8afafca75..6e49287863 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java @@ -36,7 +36,7 @@ public class ActionSendSMS extends Action { @Override public void doAction(Callback callback) { - boolean result = SmsCommunicatorPlugin.getPlugin().sendNotificationToAllNumbers(text.getValue()); + boolean result = SmsCommunicatorPlugin.INSTANCE.sendNotificationToAllNumbers(text.getValue()); if (callback != null) callback.result(new PumpEnactResult().success(result).comment(result ? R.string.ok : R.string.danar_error)).run(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java deleted file mode 100644 index 7c147a2409..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.java +++ /dev/null @@ -1,83 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - - -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.fragment.app.Fragment; - -import java.util.Collections; -import java.util.Comparator; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -public class SmsCommunicatorFragment extends Fragment { - private CompositeDisposable disposable = new CompositeDisposable(); - TextView logView; - - public SmsCommunicatorFragment() { - super(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.smscommunicator_fragment, container, false); - - logView = (TextView) view.findViewById(R.id.smscommunicator_log); - - return view; - } - - @Override - public synchronized void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventSmsCommunicatorUpdateGui.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updateGui(), FabricPrivacy::logException) - ); - updateGui(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - protected void updateGui() { - class CustomComparator implements Comparator { - public int compare(Sms object1, Sms object2) { - return (int) (object1.date - object2.date); - } - } - Collections.sort(SmsCommunicatorPlugin.getPlugin().getMessages(), new CustomComparator()); - int messagesToShow = 40; - - int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().getMessages().size() - messagesToShow); - - String logText = ""; - for (int x = start; x < SmsCommunicatorPlugin.getPlugin().getMessages().size(); x++) { - Sms sms = SmsCommunicatorPlugin.getPlugin().getMessages().get(x); - if (sms.ignored) { - logText += DateUtil.timeString(sms.date) + " <<< " + "░ " + sms.phoneNumber + " " + sms.text + "
"; - } else if (sms.received) { - logText += DateUtil.timeString(sms.date) + " <<< " + (sms.processed ? "● " : "○ ") + sms.phoneNumber + " " + sms.text + "
"; - } else if (sms.sent) { - logText += DateUtil.timeString(sms.date) + " >>> " + (sms.processed ? "● " : "○ ") + sms.phoneNumber + " " + sms.text + "
"; - } - } - logView.setText(Html.fromHtml(logText)); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt new file mode 100644 index 0000000000..90ec078931 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt @@ -0,0 +1,70 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HtmlHelper +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.smscommunicator_fragment.* +import java.util.* +import kotlin.math.max + +class SmsCommunicatorFragment : Fragment() { + private val disposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.smscommunicator_fragment, container, false) + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(toObservable(EventSmsCommunicatorUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }) { FabricPrivacy.logException(it) } + ) + updateGui() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + fun updateGui() { + class CustomComparator : Comparator { + override fun compare(object1: Sms, object2: Sms): Int { + return (object1.date - object2.date).toInt() + } + } + Collections.sort(SmsCommunicatorPlugin.messages, CustomComparator()) + val messagesToShow = 40 + val start = max(0, SmsCommunicatorPlugin.messages.size - messagesToShow) + var logText = "" + for (x in start until SmsCommunicatorPlugin.messages.size) { + val sms = SmsCommunicatorPlugin.messages[x] + when { + sms.ignored -> { + logText += DateUtil.timeString(sms.date) + " <<< " + "░ " + sms.phoneNumber + " " + sms.text + "
" + } + sms.received -> { + logText += DateUtil.timeString(sms.date) + " <<< " + (if (sms.processed) "● " else "○ ") + sms.phoneNumber + " " + sms.text + "
" + } + sms.sent -> { + logText += DateUtil.timeString(sms.date) + " >>> " + (if (sms.processed) "● " else "○ ") + sms.phoneNumber + " " + sms.text + "
" + } + } + } + smscommunicator_log?.text = HtmlHelper.fromHtml(logText) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 037f17d0bb..9361216aa0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -49,7 +49,7 @@ import java.util.* /** * Created by mike on 05.08.2016. */ -class SmsCommunicatorPlugin internal constructor() : PluginBase(PluginDescription() +object SmsCommunicatorPlugin : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(SmsCommunicatorFragment::class.java.name) .pluginName(R.string.smscommunicator) @@ -57,12 +57,17 @@ class SmsCommunicatorPlugin internal constructor() : PluginBase(PluginDescriptio .preferencesId(R.xml.pref_smscommunicator) .description(R.string.description_sms_communicator) ) { + private val log = LoggerFactory.getLogger(L.SMS) private val disposable = CompositeDisposable() var allowedNumbers: MutableList = ArrayList() var messageToConfirm: AuthRequest? = null var lastRemoteBolusTime: Long = 0 var messages = ArrayList() + init { + processSettings(null) + } + override fun onStart() { super.onStart() disposable.add(toObservable(EventPreferenceChange::class.java) @@ -772,41 +777,24 @@ class SmsCommunicatorPlugin internal constructor() : PluginBase(PluginDescriptio return passCode } - companion object { - private val log = LoggerFactory.getLogger(L.SMS) - private var smsCommunicatorPlugin: SmsCommunicatorPlugin? = null - @JvmStatic - val plugin: SmsCommunicatorPlugin? - get() { - if (smsCommunicatorPlugin == null) { - smsCommunicatorPlugin = SmsCommunicatorPlugin() - } - return smsCommunicatorPlugin - } - - private fun stripAccents(str: String): String { - var s = str - s = Normalizer.normalize(s, Normalizer.Form.NFD) - s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "") - return s - } - - fun areMoreNumbers(allowednumbers: String): Boolean { - var countNumbers = 0 - val substrings = allowednumbers.split(";").toTypedArray() - for (number in substrings) { - var cleaned = number.replace(Regex("\\s+"), "") - if (cleaned.length < 4) continue - if (cleaned.substring(0, 1).compareTo("+") != 0) continue - cleaned = cleaned.replace("+", "") - if (!cleaned.matches(Regex("[0-9]+"))) continue - countNumbers++ - } - return countNumbers > 1 - } + private fun stripAccents(str: String): String { + var s = str + s = Normalizer.normalize(s, Normalizer.Form.NFD) + s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "") + return s } - init { - processSettings(null) + fun areMoreNumbers(allowednumbers: String): Boolean { + var countNumbers = 0 + val substrings = allowednumbers.split(";").toTypedArray() + for (number in substrings) { + var cleaned = number.replace(Regex("\\s+"), "") + if (cleaned.length < 4) continue + if (cleaned.substring(0, 1).compareTo("+") != 0) continue + cleaned = cleaned.replace("+", "") + if (!cleaned.matches(Regex("[0-9]+"))) continue + countNumbers++ + } + return countNumbers > 1 } -} \ No newline at end of file + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java index 62e14285eb..b69f3f4dc7 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java @@ -50,7 +50,7 @@ public class CommandSetProfile extends Command { // Send SMS notification if ProfileSwitch is comming from NS ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis()); if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) { - SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.getPlugin(); + SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.INSTANCE; if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { smsCommunicatorPlugin.sendNotificationToAllNumbers(MainApp.gs(R.string.profile_set_ok)); } diff --git a/app/src/main/java/info/nightscout/androidaps/services/DataService.java b/app/src/main/java/info/nightscout/androidaps/services/DataService.java index 80a42e145a..44050ece35 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/DataService.java @@ -103,7 +103,7 @@ public class DataService extends IntentService { ) { handleNewDataFromNSClient(intent); } else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { - SmsCommunicatorPlugin.getPlugin().handleNewData(intent); + SmsCommunicatorPlugin.INSTANCE.handleNewData(intent); } if (L.isEnabled(L.DATASERVICE)) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 77324d9627..606bde7c80 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -758,7 +758,7 @@ public class SmsCommunicatorPluginTest { when(SmsManager.getDefault()).thenReturn(smsManager); when(SP.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678"); - smsCommunicatorPlugin = SmsCommunicatorPlugin.getPlugin(); + smsCommunicatorPlugin = SmsCommunicatorPlugin.INSTANCE; smsCommunicatorPlugin.setPluginEnabled(PluginType.GENERAL, true); mockStatic(LoopPlugin.class); From e9aa4835edee209265c5b6d0b2388ca9ab141eae Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 10:40:48 +0100 Subject: [PATCH 33/56] SBS CARBS command --- .../general/smsCommunicator/SmsAction.java | 5 + .../smsCommunicator/SmsCommunicatorPlugin.kt | 122 +++++++++++++----- .../nightscout/androidaps/utils/DateUtil.java | 6 +- app/src/main/res/values/strings.xml | 3 + app/src/test/java/info/AAPSMocker.java | 4 + .../smsCommunicator/AuthRequestTest.java | 13 +- .../SmsCommunicatorPluginTest.java | 84 +++++++++++- 7 files changed, 198 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java index 6b5d5b8747..7785b33c7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java @@ -4,6 +4,7 @@ abstract class SmsAction implements Runnable { Double aDouble; Integer anInteger; Integer secondInteger; + Long secondLong; String aString; SmsAction() {} @@ -30,4 +31,8 @@ abstract class SmsAction implements Runnable { this.anInteger = anInteger; this.secondInteger = secondInteger; } + SmsAction(Integer anInteger, Long secondLong) { + this.anInteger = anInteger; + this.secondLong = secondLong; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 9361216aa0..e1df17fd82 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -136,7 +136,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() fun isCommand(command: String, number: String): Boolean { when (command.toUpperCase(Locale.getDefault())) { - "BG", "LOOP", "TREATMENTS", "NSCLIENT", "PUMP", "BASAL", "BOLUS", "EXTENDED", "CAL", "PROFILE", "TARGET", "SMS" -> return true + "BG", "LOOP", "TREATMENTS", "NSCLIENT", "PUMP", "BASAL", "BOLUS", "EXTENDED", "CAL", "PROFILE", "TARGET", "SMS", "CARBS" -> return true } return messageToConfirm?.requester?.phoneNumber == number } @@ -208,7 +208,12 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) else if (splitted.size == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)) else if (splitted.size == 2 && pump.isSuspended) sendSMS(Sms(receivedSms.phoneNumber, R.string.pumpsuspended)) - else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "CARBS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processCARBS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) "CAL" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) else if (splitted.size == 2) processCAL(splitted, receivedSms) @@ -219,7 +224,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) "SMS" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) - else if (splitted.size == 2) processSMS(splitted, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (splitted.size == 2) processSMS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else -> if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { messageToConfirm?.action(splitted[0]) @@ -271,9 +277,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() sendSMS(Sms(receivedSms.phoneNumber, replyText)) } }) - } else { + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled)) - } receivedSms.processed = true } "ENABLE", "START" -> { @@ -282,18 +287,17 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() loopPlugin.setPluginEnabled(PluginType.LOOP, true) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled)) send(EventRefreshOverview("SMS_LOOP_START")) - } else { + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled)) - } receivedSms.processed = true } "STATUS" -> { val loopPlugin = LoopPlugin.getPlugin() val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { - if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) else MainApp.gs(R.string.smscommunicator_loopisenabled) - } else { + if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) + else MainApp.gs(R.string.smscommunicator_loopisenabled) + } else MainApp.gs(R.string.smscommunicator_loopisdisabled) - } sendSMS(Sms(receivedSms.phoneNumber, reply)) receivedSms.processed = true } @@ -348,7 +352,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() send(EventNSClientRestart()) sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")) receivedSms.processed = true - } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } private fun processNSCLIENT(splitted: Array, receivedSms: Sms) { @@ -356,7 +361,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() send(EventNSClientRestart()) sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")) receivedSms.processed = true - } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } private fun processPUMP(receivedSms: Sms) { @@ -394,7 +400,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() if (splitted[1].toUpperCase(Locale.getDefault()) == "STATUS") { sendSMS(Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().profileName)) } else if (splitted[1].toUpperCase(Locale.getDefault()) == "LIST") { - if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile)) else { + if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile)) + else { var reply = "" for (i in list.indices) { if (i > 0) reply += "\n" @@ -407,9 +414,13 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() val pindex = SafeParse.stringToInt(splitted[1]) var percentage = 100 if (splitted.size > 2) percentage = SafeParse.stringToInt(splitted[2]) - if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { val profile = store.getSpecificProfile(list[pindex - 1] as String) - if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else { + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else { val passCode = generatePasscode() val reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode) receivedSms.processed = true @@ -453,7 +464,10 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() var duration = 30 if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) val profile = ProfileFunctions.getInstance().profile - if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value() val passCode = generatePasscode() val reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) @@ -482,7 +496,10 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() var duration = 30 if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) val profile = ProfileFunctions.getInstance().profile - if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(tempBasal), profile).value() val passCode = generatePasscode() val reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) @@ -492,7 +509,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble, secondInteger, true, profile, object : Callback() { override fun run() { if (result.success) { - var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) + var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) + else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) } else { @@ -536,7 +554,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() var extended = SafeParse.stringToDouble(splitted[1]) val duration = SafeParse.stringToInt(splitted[2]) extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(extended)).value() - if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else { + if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { val passCode = generatePasscode() val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) receivedSms.processed = true @@ -569,11 +588,10 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } else if (bolus > 0.0) { val passCode = generatePasscode() - val reply = if (isMeal) { + val reply = if (isMeal) String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode) - } else { + else String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) - } receivedSms.processed = true messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) { override fun run() { @@ -587,19 +605,23 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { override fun run() { if (resultSuccess) { - var replyText = if (isMeal) { + var replyText = if (isMeal) String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered) - } else { + else String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered) - } replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) lastRemoteBolusTime = DateUtil.now() if (isMeal) { ProfileFunctions.getInstance().profile?.let { currentProfile -> var eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration) - eatingSoonTTDuration = if (eatingSoonTTDuration > 0) eatingSoonTTDuration else Constants.defaultEatingSoonTTDuration + eatingSoonTTDuration = + if (eatingSoonTTDuration > 0) eatingSoonTTDuration + else Constants.defaultEatingSoonTTDuration var eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl) - eatingSoonTT = if (eatingSoonTT > 0) eatingSoonTT else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl + eatingSoonTT = + if (eatingSoonTT > 0) eatingSoonTT + else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol + else Constants.defaultEatingSoonTTmgdl val tempTarget = TempTarget() .date(System.currentTimeMillis()) .duration(eatingSoonTTDuration) @@ -629,6 +651,47 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } + private fun processCARBS(splitted: Array, receivedSms: Sms) { + var grams = SafeParse.stringToInt(splitted[1]) + var time = DateUtil.now() + if (splitted.size > 2) { + val seconds = DateUtil.toSeconds(splitted[2].toUpperCase(Locale.getDefault())) + val midnight = MidnightTime.calc() + if (seconds == 0 && (!splitted[2].startsWith("00:00") || !splitted[2].startsWith("12:00"))) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + return + } + time = midnight + T.secs(seconds.toLong()).msecs() + } + grams = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(grams)).value() + if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_carbsreplywithcode), grams, DateUtil.timeString(time), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(grams, time) { + override fun run() { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.carbs = anInteger.toDouble() + detailedBolusInfo.date = secondLong + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + private fun processTARGET(splitted: Array, receivedSms: Sms) { val isMeal = splitted[1].equals("MEAL", ignoreCase = true) val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true) @@ -741,7 +804,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() sms.text = stripAccents(sms.text) try { if (L.isEnabled(L.SMS)) log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text) - if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) else { + if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) + else { val parts = smsManager.divideMessage(sms.text) smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, null, null) @@ -797,4 +861,4 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() } return countNumbers > 1 } - } \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index d592347d1b..4ac49d745b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -93,15 +93,15 @@ public class DateUtil { } public static int toSeconds(String hh_colon_mm) { - Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM | PM|)"); + Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)"); Matcher m = p.matcher(hh_colon_mm); int retval = 0; if (m.find()) { retval = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60; - if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM")) && m.group(1).equals("12")) + if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM") || m.group(3).equals("AM")) && m.group(1).equals("12")) retval -= 12 * 60 * 60; - if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM")) && !(m.group(1).equals("12"))) + if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM") || m.group(3).equals("PM")) && !(m.group(1).equals("12"))) retval += 12 * 60 * 60; } return retval; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2fba2a59fe..e0b9170631 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -367,10 +367,13 @@ To start basal %1$.2fU/h for %2$d min reply with code %3$s To switch profile to %1$s %2$d%% reply with code %3$s To start extended bolus %1$.2fU for %2$d min reply with code %3$s + To enter %1$dg at %2$s reply with code %3$s To start basal %1$d%% for %2$d min reply with code %3$s To suspend loop for %1$d minutes reply with code %2$s Temp basal %1$.2fU/h for %2$d min started successfully Extended bolus %1$.2fU for %2$d min started successfully + Carbs %1$dg entered successfully + Entering %1$dg of carbs failed Temp basal %1$d%% for %2$d min started successfully Temp basal start failed Extended bolus start failed diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index bd01e6b89b..510ff09c2b 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -158,6 +159,8 @@ public class AAPSMocker { when(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode)).thenReturn("To deliver meal bolus %1$.2fU reply with code %2$s"); when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered)).thenReturn("Meal Bolus %1$.2fU delivered successfully"); when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt)).thenReturn("Target %1$s for %2$d minutes"); + when(MainApp.gs(R.string.smscommunicator_carbsreplywithcode)).thenReturn("To enter %1$dg at %2$s reply with code %3$s"); + when(MainApp.gs(R.string.smscommunicator_carbsset)).thenReturn("Carbs %1$dg entered successfully"); } public static MainApp mockMainApp() { @@ -186,6 +189,7 @@ public class AAPSMocker { when(SP.getLong(anyInt(), anyLong())).thenReturn(0L); when(SP.getBoolean(anyInt(), anyBoolean())).thenReturn(false); when(SP.getInt(anyInt(), anyInt())).thenReturn(0); + when(SP.getString(anyInt(), anyString())).thenReturn(""); } public static void mockL() { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java index 7b53e214a8..5bd3cae874 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java @@ -75,12 +75,6 @@ public class AuthRequestTest { @Before public void prepareTests() { - smsCommunicatorPlugin = mock(SmsCommunicatorPlugin.class); - doAnswer((Answer) invocation -> { - sentSms = invocation.getArgument(0); - return null; - }).when(smsCommunicatorPlugin).sendSMS(any(Sms.class)); - AAPSMocker.mockMainApp(); AAPSMocker.mockApplicationContext(); AAPSMocker.mockSP(); @@ -88,5 +82,12 @@ public class AuthRequestTest { AAPSMocker.mockStrings(); mockStatic(DateUtil.class); + + smsCommunicatorPlugin = mock(SmsCommunicatorPlugin.class); + doAnswer((Answer) invocation -> { + sentSms = invocation.getArgument(0); + return null; + }).when(smsCommunicatorPlugin).sendSMS(any(Sms.class)); + } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 606bde7c80..76d8e5dda3 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -49,6 +49,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.powermock.api.mockito.PowerMockito.doAnswer; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.when; @RunWith(PowerMockRunner.class) @@ -720,6 +721,87 @@ public class SmsCommunicatorPluginTest { Assert.assertEquals("Calibration sent. Receiving must be enabled in xDrip.", smsCommunicatorPlugin.getMessages().get(3).text); } + @Test + public void processCarbsTest() { + Sms sms; + + when(DateUtil.now()).thenReturn(1000000L); + when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(false); + //CAL + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + + when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); + + //CARBS + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + + when(MainApp.getConstraintChecker().applyCarbsConstraints(any())).thenReturn(new Constraint<>(0)); + + //CARBS 0 + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 0"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 0", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + + when(MainApp.getConstraintChecker().applyCarbsConstraints(any())).thenReturn(new Constraint<>(1)); + + //CARBS 1 + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 1"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 1", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 01:16AM reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + + //CARBS 1 a + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 1 a"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 1 a", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Wrong format")); + + //CARBS 1 00 + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 1 00"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 1 00", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Wrong format")); + + //CARBS 1 12:01 + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 1 12:01"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 1 12:01", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 12:01PM reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + + //CARBS 1 3:01AM + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "CARBS 1 3:01AM"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("CARBS 1 3:01AM", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 03:01AM reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + } + @Test public void sendNotificationToAllNumbers() { smsCommunicatorPlugin.setMessages(new ArrayList<>()); @@ -752,7 +834,7 @@ public class SmsCommunicatorPluginTest { PowerMockito.when(IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB")).thenReturn(new CobInfo(10d, 2d)); mockStatic(XdripCalibrations.class); - mockStatic(DateUtil.class); + spy(DateUtil.class); mockStatic(SmsManager.class); SmsManager smsManager = mock(SmsManager.class); when(SmsManager.getDefault()).thenReturn(smsManager); From 17ea3843ae4ff32ee6bd93384ef1ea9e000e7003 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 11:19:09 +0100 Subject: [PATCH 34/56] SMS HELP command --- .../smsCommunicator/SmsCommunicatorPlugin.kt | 41 ++++++++++++++++--- .../SmsCommunicatorPluginTest.java | 13 ++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index e1df17fd82..b033315439 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -46,9 +46,6 @@ import org.slf4j.LoggerFactory import java.text.Normalizer import java.util.* -/** - * Created by mike on 05.08.2016. - */ object SmsCommunicatorPlugin : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(SmsCommunicatorFragment::class.java.name) @@ -64,6 +61,23 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() var lastRemoteBolusTime: Long = 0 var messages = ArrayList() + val commands = mapOf( + "BG" to "BG", + "LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS\nLOOP SUSPEND 20", + "TREATMENTS" to "TREATMENTS REFRESH", + "NSCLIENT" to "NSCLIENT RESTART", + "PUMP" to "PUMP", + "BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n", + "BOLUS" to "BOLUS 1.2\nBOLUS 1.2 MEAL", + "EXTENDED" to "EXTENDED STOP/CANCEL\nEXTENDED 2 120", + "CAL" to "CAL 5.6", + "PROFILE" to "PROFILE STATUS/LIST\nPROFILE 1\nPROFILE 2 30", + "TARGET" to "TARGET MEAL/ACTIVITY/HYPO/STOP", + "SMS" to "SMS DISABLE/STOP", + "CARBS" to "CARBS 12\nCARBS 12 23:05\nCARBS 12 11:05PM", + "HELP" to "HELP\nHELP command" + ) + init { processSettings(null) } @@ -135,10 +149,11 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() } fun isCommand(command: String, number: String): Boolean { - when (command.toUpperCase(Locale.getDefault())) { - "BG", "LOOP", "TREATMENTS", "NSCLIENT", "PUMP", "BASAL", "BOLUS", "EXTENDED", "CAL", "PROFILE", "TARGET", "SMS", "CARBS" -> return true + var found = false + commands.forEach { (k, _) -> + if (k == command) found = true } - return messageToConfirm?.requester?.phoneNumber == number + return found || messageToConfirm?.requester?.phoneNumber == number } fun isAllowedNumber(number: String): Boolean { @@ -226,6 +241,9 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) else if (splitted.size == 2) processSMS(splitted, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "HELP" -> + if (splitted.size == 1 || splitted.size == 2) processHELP(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) else -> if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { messageToConfirm?.action(splitted[0]) @@ -365,6 +383,17 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } + private fun processHELP(splitted: Array, receivedSms: Sms) { + if (splitted.size == 1) { + sendSMS(Sms(receivedSms.phoneNumber, commands.keys.toString().replace("[", "").replace("]", ""))) + receivedSms.processed = true + } else if (isCommand(splitted[1].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { + sendSMS(Sms(receivedSms.phoneNumber, commands[splitted[1].toUpperCase(Locale.getDefault())])) + receivedSms.processed = true + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + private fun processPUMP(receivedSms: Sms) { ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { override fun run() { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 76d8e5dda3..190adf7b42 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -348,6 +348,19 @@ public class SmsCommunicatorPluginTest { Assert.assertEquals("PUMP", smsCommunicatorPlugin.getMessages().get(0).text); Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.getMessages().get(1).text); + //HELP + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "HELP"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("HELP", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("PUMP")); + + //HELP PUMP + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "HELP PUMP"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("HELP PUMP", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("PUMP")); } @Test From 820b704bfed1bb63c913508746fb74c65d9d9ded Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 11:40:51 +0100 Subject: [PATCH 35/56] fix test --- .../general/smsCommunicator/SmsCommunicatorPluginTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 190adf7b42..0cfc4e4cda 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -772,7 +772,7 @@ public class SmsCommunicatorPluginTest { sms = new Sms("1234", "CARBS 1"); smsCommunicatorPlugin.processSms(sms); Assert.assertEquals("CARBS 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 01:16AM reply with code")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at")); String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); From ebc89e752669397abb50f74aa4c2ddbe33e8e678 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Fri, 22 Nov 2019 12:31:59 +0100 Subject: [PATCH 36/56] New tests for SMS commands: TARGET MEAL/HYPO/ACTIVITY and SMS STOP/DISABLE --- .../smsCommunicator/SmsCommunicatorPlugin.kt | 1 - .../SmsCommunicatorPluginTest.java | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index e1df17fd82..8475a2511d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -854,7 +854,6 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() for (number in substrings) { var cleaned = number.replace(Regex("\\s+"), "") if (cleaned.length < 4) continue - if (cleaned.substring(0, 1).compareTo("+") != 0) continue cleaned = cleaned.replace("+", "") if (!cleaned.matches(Regex("[0-9]+"))) continue countNumbers++ diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 76d8e5dda3..350bf4b504 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -348,6 +348,43 @@ public class SmsCommunicatorPluginTest { Assert.assertEquals("PUMP", smsCommunicatorPlugin.getMessages().get(0).text); Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.getMessages().get(1).text); + //SMS : wrong format + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "SMS"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertFalse(sms.ignored); + Assert.assertEquals("SMS", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + + //SMS STOP + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "SMS DISABLE"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("SMS DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To disable the SMS Remote Service reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone.")); + + //TARGET : wrong format + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "TARGET"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertFalse(sms.ignored); + Assert.assertEquals("TARGET", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + + //TARGET MEAL + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "TARGET MEAL"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("TARGET MEAL", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To set the Temp Target MEAL reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Target MEAL for 45 minutes set successfully")); } @Test From f26e683dbecb3f6daffe4a013305def8ef4b92a9 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Fri, 22 Nov 2019 15:58:03 +0100 Subject: [PATCH 37/56] Removed + control for Phone Numbers, Added Tests for TT, Added TARGET STOP/CANCEL command --- .../smsCommunicator/SmsCommunicatorPlugin.kt | 27 ++++++++++++++++++- app/src/main/res/values/strings.xml | 2 ++ app/src/test/java/info/AAPSMocker.java | 7 +++++ .../SmsCommunicatorPluginTest.java | 15 +++++++++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index ca288da057..63848dd915 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -39,6 +39,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorP import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil.now import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import org.apache.commons.lang3.StringUtils @@ -725,6 +726,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() val isMeal = splitted[1].equals("MEAL", ignoreCase = true) val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true) val isHypo = splitted[1].equals("HYPO", ignoreCase = true) + val isStop = splitted[1].equals("STOP", ignoreCase = true) || splitted[1].equals("CANCEL", ignoreCase = true) if (isMeal || isActivity || isHypo) { val passCode = generatePasscode() val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode) @@ -777,7 +779,30 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() } } }) - } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else if (isStop) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetcancel), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + val currentProfile = ProfileFunctions.getInstance().profile + if (currentProfile != null) { + val tempTarget = TempTarget() + .source(Source.USER) + .date(now()) + .duration(0) + .low(0.0) + .high(0.0) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_canceled)) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)) + } + } + }) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } private fun processSMS(splitted: Array, receivedSms: Sms) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e0b9170631..f65eadd13f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -294,6 +294,7 @@ To deliver bolus %1$.2fU reply with code %2$s To deliver meal bolus %1$.2fU reply with code %2$s To set the Temp Target %1$s reply with code %2$s + To cancel Temp Target reply with code %1$s To disable the SMS Remote Service reply with code %1$s.\n\nKeep in mind that you\'ll able to reactivate it directly from the AAPS master smartphone only. SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone. To send calibration %1$.2f reply with code %2$s @@ -308,6 +309,7 @@ Meal Bolus %1$.2fU delivered successfully Target %1$s for %2$d minutes Target %1$s for %2$d minutes set successfully + Temp Target canceled successfully Delivering %1$.2fU Allow remote commands via SMS Finger diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index 510ff09c2b..8385b27701 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -115,6 +115,13 @@ public class AAPSMocker { when(MainApp.gs(R.string.sms_lastbg)).thenReturn("Last BG:"); when(MainApp.gs(R.string.sms_minago)).thenReturn("%1$dmin ago"); when(MainApp.gs(R.string.smscommunicator_remotecommandnotallowed)).thenReturn("Remote command is not allowed"); + when(MainApp.gs(R.string.smscommunicator_stopsmswithcode)).thenReturn("To disable the SMS Remote Service reply with code %1$s.\\n\\nKeep in mind that you\\'ll able to reactivate it directly from the AAPS master smartphone only."); + when(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode)).thenReturn("To deliver meal bolus %1$.2fU reply with code %2$s."); + when(MainApp.gs(R.string.smscommunicator_temptargetwithcode)).thenReturn("To set the Temp Target %1$s reply with code %2$s"); + when(MainApp.gs(R.string.smscommunicator_temptargetcancel)).thenReturn("To cancel Temp Target reply with code %1$s"); + when(MainApp.gs(R.string.smscommunicator_stoppedsms)).thenReturn("SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone."); + when(MainApp.gs(R.string.smscommunicator_tt_set)).thenReturn("Target %1$s for %2$d minutes set successfully"); + when(MainApp.gs(R.string.smscommunicator_tt_canceled)).thenReturn("Temp Target canceled successfully"); when(MainApp.gs(R.string.loopsuspendedfor)).thenReturn("Suspended (%1$d m)"); when(MainApp.gs(R.string.smscommunicator_loopisdisabled)).thenReturn("Loop is disabled"); when(MainApp.gs(R.string.smscommunicator_loopisenabled)).thenReturn("Loop is enabled"); diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index ef43609026..2076991c89 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -394,11 +394,22 @@ public class SmsCommunicatorPluginTest { sms = new Sms("1234", "TARGET MEAL"); smsCommunicatorPlugin.processSms(sms); Assert.assertEquals("TARGET MEAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To set the Temp Target MEAL reply with code")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To set the Temp Target")); passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Target MEAL for 45 minutes set successfully")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("set successfully")); + + //TARGET STOP/CANCEL + smsCommunicatorPlugin.setMessages(new ArrayList<>()); + sms = new Sms("1234", "TARGET STOP"); + smsCommunicatorPlugin.processSms(sms); + Assert.assertEquals("TARGET STOP", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To cancel Temp Target reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Temp Target canceled successfully")); } @Test From fa765708f1f6f3ea7891101af1ab06dc642b4f9a Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 22 Nov 2019 16:10:27 +0100 Subject: [PATCH 38/56] fix test --- app/src/test/java/info/AAPSMocker.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index a36c989b11..ed9f2c82a2 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -107,6 +107,8 @@ public class AAPSMocker { when(MainApp.gs(R.string.configbuilder_insulin)).thenReturn("Insulin"); when(MainApp.gs(R.string.bolusdelivering)).thenReturn("Delivering 0.0U"); when(MainApp.gs(R.string.profile_per_unit)).thenReturn("/U"); + when(MainApp.gs(R.string.shortday)).thenReturn("d"); + when(MainApp.gs(R.string.shorthour)).thenReturn("h"); when(MainApp.gs(R.string.profile_carbs_per_unit)).thenReturn("g/U"); when(MainApp.gs(R.string.profile_ins_units_per_hour)).thenReturn("U/h"); when(MainApp.gs(R.string.sms_wrongcode)).thenReturn("Wrong code. Command cancelled."); From 46270d87e2052db695e3b6ffc56abfbf34ea5f1f Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Fri, 22 Nov 2019 16:41:09 +0100 Subject: [PATCH 39/56] Removed + control for Phone Numbers, Added Tests for TT, Added TARGET STOP/CANCEL command --- .../plugins/general/smsCommunicator/AuthRequest.java | 3 ++- .../plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java index 45cd824743..40216dda30 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java @@ -37,8 +37,9 @@ public class AuthRequest { log.debug("Already processed"); return; } + //if (false) { //F. Casellato - I test SMS with master phone that sends commands to itself. I found only this way to di this... if (!confirmCode.equals(codeReceived)) { - processed = true; + processed = true; if (L.isEnabled(L.SMS)) log.debug("Wrong code"); plugin.sendSMS(new Sms(requester.phoneNumber, R.string.sms_wrongcode)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 63848dd915..4094cc1c39 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -887,6 +887,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() private fun generatePasscode(): String { val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :) var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar()) + //For Milos: may we have only 'a'.toInt() for the 2nd and 3rd chars? Upper letters are uncomfortable here val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar()) val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() From 1ea25b68a8619ef59977fcf8cc4cab5542cf5ad3 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 25 Nov 2019 17:06:08 +0100 Subject: [PATCH 40/56] fix NPE --- .../smsCommunicator/SmsCommunicatorPlugin.kt | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 4094cc1c39..c614d9cea9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -39,7 +39,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorP import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.utils.DateUtil.now import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import org.apache.commons.lang3.StringUtils @@ -98,10 +97,22 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() override fun preprocessPreferences(preferenceFragment: PreferenceFragment) { super.preprocessPreferences(preferenceFragment) - val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference - val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference - if (distance != null && allowedNumbers != null) { - if (!areMoreNumbers(allowedNumbers.text)) { + val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference? + ?: return + val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference? + ?: return + if (!areMoreNumbers(allowedNumbers.text)) { + distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) + distance.isEnabled = false + } else { + distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + distance.isEnabled = true + } + allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { preference: Preference?, newValue: Any -> + if (!areMoreNumbers(newValue as String)) { + distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString() distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + ".\n" + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) @@ -110,19 +121,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) distance.isEnabled = true } - allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { preference: Preference?, newValue: Any -> - if (!areMoreNumbers(newValue as String)) { - distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString() - distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) - + ".\n" - + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) - distance.isEnabled = false - } else { - distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) - distance.isEnabled = true - } - true - } + true } } @@ -166,9 +165,10 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() fun handleNewData(intent: Intent) { val bundle = intent.extras ?: return + val format = bundle.getString("format") ?: return val pdus = bundle["pdus"] as Array<*> for (pdu in pdus) { - val message = SmsMessage.createFromPdu(pdu as ByteArray) + val message = SmsMessage.createFromPdu(pdu as ByteArray, format) processSms(Sms(message)) } } @@ -787,9 +787,9 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() override fun run() { val currentProfile = ProfileFunctions.getInstance().profile if (currentProfile != null) { - val tempTarget = TempTarget() + val tempTarget = TempTarget() .source(Source.USER) - .date(now()) + .date(DateUtil.now()) .duration(0) .low(0.0) .high(0.0) @@ -887,7 +887,6 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() private fun generatePasscode(): String { val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :) var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar()) - //For Milos: may we have only 'a'.toInt() for the 2nd and 3rd chars? Upper letters are uncomfortable here val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar()) val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() From c33e3994b677fa5e499bddcebed90fc50c17cb7f Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 25 Nov 2019 18:07:16 +0100 Subject: [PATCH 41/56] some cleanup and kotlin conversion --- .../objectives/ObjectivesPlugin.kt | 2 +- .../general/smsCommunicator/AuthRequest.java | 60 --- .../general/smsCommunicator/AuthRequest.kt | 38 ++ .../plugins/general/smsCommunicator/Sms.java | 42 -- .../plugins/general/smsCommunicator/Sms.kt | 40 ++ .../general/smsCommunicator/SmsAction.java | 38 -- .../general/smsCommunicator/SmsAction.kt | 68 +++ .../smsCommunicator/SmsCommunicatorPlugin.kt | 51 +- .../smsCommunicator/AuthRequestTest.java | 8 +- .../smsCommunicator/SmsActionTest.java | 16 +- .../SmsCommunicatorPluginTest.java | 468 +++++++++--------- .../general/smsCommunicator/SmsTest.java | 18 +- 12 files changed, 430 insertions(+), 419 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.kt diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index ca3e1d3126..5882f84d61 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -105,7 +105,7 @@ object ObjectivesPlugin : PluginBase(PluginDescription() val requestCode = SP.getString(R.string.key_objectives_request_code, "") var url = SP.getString(R.string.key_nsclientinternal_url, "").toLowerCase() if (!url.endsWith("\"")) url = "$url/" - val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() + @Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { SP.putLong("Objectives_" + "openloop" + "_started", DateUtil.now()) SP.putLong("Objectives_" + "openloop" + "_accomplished", DateUtil.now()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java deleted file mode 100644 index 40216dda30..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.java +++ /dev/null @@ -1,60 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.utils.DateUtil; - -public class AuthRequest { - private static Logger log = LoggerFactory.getLogger(L.SMS); - - Sms requester; - String confirmCode; - private Runnable action; - - private long date; - - private boolean processed; - private SmsCommunicatorPlugin plugin; - - AuthRequest(SmsCommunicatorPlugin plugin, Sms requester, String requestText, String confirmCode, SmsAction action) { - this.requester = requester; - this.confirmCode = confirmCode; - this.action = action; - this.plugin = plugin; - - this.date = DateUtil.now(); - - plugin.sendSMS(new Sms(requester.phoneNumber, requestText)); - } - - void action(String codeReceived) { - if (processed) { - if (L.isEnabled(L.SMS)) - log.debug("Already processed"); - return; - } - //if (false) { //F. Casellato - I test SMS with master phone that sends commands to itself. I found only this way to di this... - if (!confirmCode.equals(codeReceived)) { - processed = true; - if (L.isEnabled(L.SMS)) - log.debug("Wrong code"); - plugin.sendSMS(new Sms(requester.phoneNumber, R.string.sms_wrongcode)); - return; - } - if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { - processed = true; - if (L.isEnabled(L.SMS)) - log.debug("Processing confirmed SMS: " + requester.text); - if (action != null) - action.run(); - return; - } - if (L.isEnabled(L.SMS)) - log.debug("Timed out SMS: " + requester.text); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt new file mode 100644 index 0000000000..48e816928f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.utils.DateUtil +import org.slf4j.LoggerFactory + +class AuthRequest internal constructor(val plugin: SmsCommunicatorPlugin, var requester: Sms, requestText: String, var confirmCode: String, val action: SmsAction) { + private val log = LoggerFactory.getLogger(L.SMS) + + private val date = DateUtil.now() + private var processed = false + + init { + plugin.sendSMS(Sms(requester.phoneNumber, requestText)) + } + + fun action(codeReceived: String) { + if (processed) { + if (L.isEnabled(L.SMS)) log.debug("Already processed") + return + } + if (confirmCode != codeReceived) { + processed = true + if (L.isEnabled(L.SMS)) log.debug("Wrong code") + plugin.sendSMS(Sms(requester.phoneNumber, R.string.sms_wrongcode)) + return + } + if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { + processed = true + if (L.isEnabled(L.SMS)) log.debug("Processing confirmed SMS: " + requester.text) + action.run() + return + } + if (L.isEnabled(L.SMS)) log.debug("Timed out SMS: " + requester.text) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java deleted file mode 100644 index 8efeb909fc..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.java +++ /dev/null @@ -1,42 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -import android.telephony.SmsMessage; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.utils.DateUtil; - -public class Sms { - String phoneNumber; - String text; - long date; - boolean received = false; - boolean sent = false; - boolean processed = false; - boolean ignored = false; - - Sms(SmsMessage message) { - phoneNumber = message.getOriginatingAddress(); - text = message.getMessageBody(); - date = message.getTimestampMillis(); - received = true; - } - - Sms(String phoneNumber, String text) { - this.phoneNumber = phoneNumber; - this.text = text; - this.date = DateUtil.now(); - sent = true; - } - - Sms(String phoneNumber, int textId) { - this.phoneNumber = phoneNumber; - this.text = MainApp.gs(textId); - this.date = DateUtil.now(); - sent = true; - } - - public String toString() { - return "SMS from " + phoneNumber + ": " + text; - } -} - diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt new file mode 100644 index 0000000000..868620be2e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt @@ -0,0 +1,40 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import android.telephony.SmsMessage +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.utils.DateUtil + +class Sms { + var phoneNumber: String + var text: String + var date: Long + var received = false + var sent = false + var processed = false + var ignored = false + + internal constructor(message: SmsMessage) { + phoneNumber = message.originatingAddress ?: "" + text = message.messageBody + date = message.timestampMillis + received = true + } + + internal constructor(phoneNumber: String, text: String) { + this.phoneNumber = phoneNumber + this.text = text + date = DateUtil.now() + sent = true + } + + internal constructor(phoneNumber: String, textId: Int) { + this.phoneNumber = phoneNumber + text = MainApp.gs(textId) + date = DateUtil.now() + sent = true + } + + override fun toString(): String { + return "SMS from $phoneNumber: $text" + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java deleted file mode 100644 index 7785b33c7c..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -abstract class SmsAction implements Runnable { - Double aDouble; - Integer anInteger; - Integer secondInteger; - Long secondLong; - String aString; - - SmsAction() {} - - SmsAction(Double aDouble) { - this.aDouble = aDouble; - } - - SmsAction(Double aDouble, Integer secondInteger) { - this.aDouble = aDouble; - this.secondInteger = secondInteger; - } - - SmsAction(String aString, Integer secondInteger) { - this.aString = aString; - this.secondInteger = secondInteger; - } - - SmsAction(Integer anInteger) { - this.anInteger = anInteger; - } - - SmsAction(Integer anInteger, Integer secondInteger) { - this.anInteger = anInteger; - this.secondInteger = secondInteger; - } - SmsAction(Integer anInteger, Long secondLong) { - this.anInteger = anInteger; - this.secondLong = secondLong; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.kt new file mode 100644 index 0000000000..98c892d918 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsAction.kt @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +abstract class SmsAction : Runnable { + var aDouble: Double? = null + var anInteger: Int? = null + var secondInteger: Int? = null + var secondLong: Long? = null + var aString: String? = null + + internal constructor() + internal constructor(aDouble: Double) { + this.aDouble = aDouble + } + + internal constructor(aDouble: Double, secondInteger: Int) { + this.aDouble = aDouble + this.secondInteger = secondInteger + } + + internal constructor(aString: String, secondInteger: Int) { + this.aString = aString + this.secondInteger = secondInteger + } + + internal constructor(anInteger: Int) { + this.anInteger = anInteger + } + + internal constructor(anInteger: Int, secondInteger: Int) { + this.anInteger = anInteger + this.secondInteger = secondInteger + } + + internal constructor(anInteger: Int, secondLong: Long) { + this.anInteger = anInteger + this.secondLong = secondLong + } + + fun aDouble(): Double { + return aDouble?.let { + aDouble + } ?: throw IllegalStateException() + } + + fun anInteger(): Int { + return anInteger?.let { + anInteger + } ?: throw IllegalStateException() + } + + fun secondInteger(): Int { + return secondInteger?.let { + secondInteger + } ?: throw IllegalStateException() + } + + fun secondLong(): Long { + return secondLong?.let { + secondLong + } ?: throw IllegalStateException() + } + + fun aString(): String { + return aString?.let { + aString + } ?: throw IllegalStateException() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index c614d9cea9..e40ed8b1df 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -110,7 +110,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) distance.isEnabled = true } - allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { preference: Preference?, newValue: Any -> + allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { _: Preference?, newValue: Any -> if (!areMoreNumbers(newValue as String)) { distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString() distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) @@ -344,8 +344,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (result.success) { - LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000) - NSUpload.uploadOpenAPSOffline(anInteger * 60.toDouble()) + LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger() * 60L * 1000) + NSUpload.uploadOpenAPSOffline(anInteger() * 60.toDouble()) send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) val replyText = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " + MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) @@ -389,8 +389,10 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() sendSMS(Sms(receivedSms.phoneNumber, commands.keys.toString().replace("[", "").replace("]", ""))) receivedSms.processed = true } else if (isCommand(splitted[1].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { - sendSMS(Sms(receivedSms.phoneNumber, commands[splitted[1].toUpperCase(Locale.getDefault())])) - receivedSms.processed = true + commands[splitted[1].toUpperCase(Locale.getDefault())]?.let { + sendSMS(Sms(receivedSms.phoneNumber, it)) + receivedSms.processed = true + } } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } @@ -504,7 +506,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() receivedSms.processed = true messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { override fun run() { - ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger, secondInteger, true, profile, object : Callback() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() { override fun run() { if (result.success) { var replyText: String @@ -536,7 +538,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() receivedSms.processed = true messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { override fun run() { - ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble, secondInteger, true, profile, object : Callback() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() { override fun run() { if (result.success) { var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) @@ -591,7 +593,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() receivedSms.processed = true messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(extended, duration) { override fun run() { - ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble, secondInteger, object : Callback() { + ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() { override fun run() { if (result.success) { var replyText = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration) @@ -626,7 +628,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) { override fun run() { val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.insulin = aDouble + detailedBolusInfo.insulin = aDouble() detailedBolusInfo.source = Source.USER ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { @@ -702,8 +704,8 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(grams, time) { override fun run() { val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.carbs = anInteger.toDouble() - detailedBolusInfo.date = secondLong + detailedBolusInfo.carbs = anInteger().toDouble() + detailedBolusInfo.date = secondLong() ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (result.success) { @@ -837,7 +839,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) } - fun sendNotificationToAllNumbers(text: String?): Boolean { + fun sendNotificationToAllNumbers(text: String): Boolean { var result = true for (i in allowedNumbers.indices) { val sms = Sms(allowedNumbers[i], text) @@ -902,16 +904,19 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() return s } - fun areMoreNumbers(allowednumbers: String): Boolean { - var countNumbers = 0 - val substrings = allowednumbers.split(";").toTypedArray() - for (number in substrings) { - var cleaned = number.replace(Regex("\\s+"), "") - if (cleaned.length < 4) continue - cleaned = cleaned.replace("+", "") - if (!cleaned.matches(Regex("[0-9]+"))) continue - countNumbers++ - } - return countNumbers > 1 + fun areMoreNumbers(allowednumbers: String?): Boolean { + return allowednumbers?.let { + var countNumbers = 0 + val substrings = it.split(";").toTypedArray() + for (number in substrings) { + var cleaned = number.replace(Regex("\\s+"), "") + if (cleaned.length < 4) continue + cleaned = cleaned.replace("+", "") + cleaned = cleaned.replace("-", "") + if (!cleaned.matches(Regex("[0-9]+"))) continue + countNumbers++ + } + countNumbers > 1 + } ?: false } } \ No newline at end of file diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java index 5bd3cae874..4d0ed00415 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java @@ -43,14 +43,14 @@ public class AuthRequestTest { // Check if SMS requesting code is sent AuthRequest authRequest = new AuthRequest(smsCommunicatorPlugin, requester, "Request text", "ABC", action); - Assert.assertEquals(sentSms.phoneNumber, "aNumber"); - Assert.assertEquals(sentSms.text, "Request text"); + Assert.assertEquals(sentSms.getPhoneNumber(), "aNumber"); + Assert.assertEquals(sentSms.getText(), "Request text"); // wrong reply actionCalled = false; authRequest.action("EFG"); - Assert.assertEquals(sentSms.phoneNumber, "aNumber"); - Assert.assertEquals(sentSms.text, "Wrong code. Command cancelled."); + Assert.assertEquals(sentSms.getPhoneNumber(), "aNumber"); + Assert.assertEquals(sentSms.getText(), "Wrong code. Command cancelled."); Assert.assertFalse(actionCalled); // correct reply diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsActionTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsActionTest.java index f4d61e56eb..f6a4712b72 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsActionTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsActionTest.java @@ -41,7 +41,7 @@ public class SmsActionTest { }; smsAction.run(); Assert.assertEquals(result, "B"); - Assert.assertEquals(smsAction.aDouble, 1d, 0.000001d); + Assert.assertEquals(smsAction.aDouble(), 1d, 0.000001d); smsAction = new SmsAction(1d, 2) { @Override @@ -51,8 +51,8 @@ public class SmsActionTest { }; smsAction.run(); Assert.assertEquals(result, "C"); - Assert.assertEquals(smsAction.aDouble, 1d, 0.000001d); - Assert.assertEquals(smsAction.secondInteger.intValue(), 2); + Assert.assertEquals(smsAction.aDouble(), 1d, 0.000001d); + Assert.assertEquals(smsAction.secondInteger(), 2); smsAction = new SmsAction("aString", 3) { @Override @@ -62,8 +62,8 @@ public class SmsActionTest { }; smsAction.run(); Assert.assertEquals(result, "D"); - Assert.assertEquals(smsAction.aString, "aString"); - Assert.assertEquals(smsAction.secondInteger.intValue(), 3); + Assert.assertEquals(smsAction.aString(), "aString"); + Assert.assertEquals(smsAction.secondInteger(), 3); smsAction = new SmsAction(4) { @Override @@ -73,7 +73,7 @@ public class SmsActionTest { }; smsAction.run(); Assert.assertEquals(result, "E"); - Assert.assertEquals(smsAction.anInteger.intValue(), 4); + Assert.assertEquals(smsAction.anInteger(), 4); smsAction = new SmsAction(5, 6) { @Override @@ -83,8 +83,8 @@ public class SmsActionTest { }; smsAction.run(); Assert.assertEquals(result, "F"); - Assert.assertEquals(smsAction.anInteger.intValue(), 5); - Assert.assertEquals(smsAction.secondInteger.intValue(), 6); + Assert.assertEquals(smsAction.anInteger(), 5); + Assert.assertEquals(smsAction.secondInteger(), 6); } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java index 2076991c89..c757840c1f 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.java @@ -106,32 +106,32 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("12", "aText"); smsCommunicatorPlugin.processSms(sms); - Assert.assertTrue(sms.ignored); - Assert.assertEquals("aText", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertTrue(sms.getIgnored()); + Assert.assertEquals("aText", smsCommunicatorPlugin.getMessages().get(0).getText()); //UNKNOWN smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "UNKNOWN"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("UNKNOWN", smsCommunicatorPlugin.getMessages().get(0).text); + Assert.assertEquals("UNKNOWN", smsCommunicatorPlugin.getMessages().get(0).getText()); //BG smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BG"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BG", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("IOB:")); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Last BG: 100")); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("COB: 10(2)g")); + Assert.assertEquals("BG", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("IOB:")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("Last BG: 100")); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("COB: 10(2)g")); // LOOP : test remote control disabled when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(false); smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Remote command is not allowed")); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("Remote command is not allowed")); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); //LOOP STATUS : disabled @@ -139,8 +139,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP STATUS : suspended when(loopPlugin.minutesToEndOfSuspend()).thenReturn(10); @@ -149,8 +149,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP STATUS : enabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -158,27 +158,27 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP STATUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP : wrong format when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP DISABLE : already disabled when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false); smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP DISABLE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop is disabled", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP DISABLE : from enabled hasBeenRun = false; @@ -190,9 +190,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP DISABLE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP DISABLE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.getMessages().get(1).getText()); Assert.assertTrue(hasBeenRun); //LOOP ENABLE : already enabled @@ -200,9 +200,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP ENABLE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP ENABLE : from disabled hasBeenRun = false; @@ -214,72 +214,72 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP ENABLE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP ENABLE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.getMessages().get(1).getText()); Assert.assertTrue(hasBeenRun); //LOOP RESUME : already enabled smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP RESUME"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP RESUME", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Loop resumed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP RESUME", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Loop resumed", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP SUSPEND 1 2: wrong format smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 1 2"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 1 2", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP SUSPEND 1 2", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP SUSPEND 0 : wrong duration smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP SUSPEND 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.getMessages().get(1).getText()); //LOOP SUSPEND 100 : suspend for 100 min + correct answer smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 100"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 100", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To suspend loop for 100 minutes reply with code ")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP SUSPEND 100", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To suspend loop for 100 minutes reply with code ")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Loop suspended Temp basal canceled", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Loop suspended Temp basal canceled", smsCommunicatorPlugin.getMessages().get(3).getText()); //LOOP SUSPEND 200 : limit to 180 min + wrong answer smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP SUSPEND 200"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP SUSPEND 200", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To suspend loop for 180 minutes reply with code ")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP SUSPEND 200", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To suspend loop for 180 minutes reply with code ")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); // ignore from other number smsCommunicatorPlugin.processSms(new Sms("5678", passCode)); smsCommunicatorPlugin.processSms(new Sms("1234", "XXXX")); - Assert.assertEquals("XXXX", smsCommunicatorPlugin.getMessages().get(3).text); - Assert.assertEquals("Wrong code. Command cancelled.", smsCommunicatorPlugin.getMessages().get(4).text); + Assert.assertEquals("XXXX", smsCommunicatorPlugin.getMessages().get(3).getText()); + Assert.assertEquals("Wrong code. Command cancelled.", smsCommunicatorPlugin.getMessages().get(4).getText()); //then correct code should not work smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(5).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(5).getText()); Assert.assertEquals(6, smsCommunicatorPlugin.getMessages().size()); // processed as common message //LOOP BLABLA smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "LOOP BLABLA"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("LOOP BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("LOOP BLABLA", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //TREATMENTS REFRESH when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -287,9 +287,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS REFRESH"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS REFRESH", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("TREATMENTS REFRESH")); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("TREATMENTS REFRESH", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("TREATMENTS REFRESH")); //TREATMENTS BLA BLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -297,9 +297,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS BLA BLA"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS BLA BLA", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("TREATMENTS BLA BLA", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //TREATMENTS BLABLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -307,9 +307,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TREATMENTS BLABLA"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("TREATMENTS BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("TREATMENTS BLABLA", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //NSCLIENT RESTART when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -317,9 +317,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT RESTART"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT RESTART", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("NSCLIENT RESTART")); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("NSCLIENT RESTART", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("NSCLIENT RESTART")); //NSCLIENT BLA BLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -327,9 +327,9 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT BLA BLA"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT BLA BLA", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("NSCLIENT BLA BLA", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //NSCLIENT BLABLA when(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true); @@ -337,79 +337,79 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "NSCLIENT BLABLA"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("NSCLIENT BLABLA", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("NSCLIENT BLABLA", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //PUMP smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PUMP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PUMP", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PUMP", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Virtual Pump", smsCommunicatorPlugin.getMessages().get(1).getText()); //HELP smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "HELP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("HELP", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("PUMP")); + Assert.assertEquals("HELP", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("PUMP")); //HELP PUMP smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "HELP PUMP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("HELP PUMP", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("PUMP")); + Assert.assertEquals("HELP PUMP", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("PUMP")); //SMS : wrong format smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "SMS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("SMS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("SMS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //SMS STOP smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "SMS DISABLE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("SMS DISABLE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To disable the SMS Remote Service reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("SMS DISABLE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To disable the SMS Remote Service reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone.")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone.")); //TARGET : wrong format smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TARGET"); smsCommunicatorPlugin.processSms(sms); - Assert.assertFalse(sms.ignored); - Assert.assertEquals("TARGET", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertFalse(sms.getIgnored()); + Assert.assertEquals("TARGET", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //TARGET MEAL smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TARGET MEAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("TARGET MEAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To set the Temp Target")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("TARGET MEAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To set the Temp Target")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("set successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("set successfully")); //TARGET STOP/CANCEL smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "TARGET STOP"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("TARGET STOP", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To cancel Temp Target reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("TARGET STOP", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To cancel Temp Target reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Temp Target canceled successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("Temp Target canceled successfully")); } @Test @@ -420,8 +420,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -429,15 +429,15 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE LIST (no profile interface) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).getText()); ProfileInterface profileInterface = mock(SimpleProfilePlugin.class); when(ConfigBuilderPlugin.getPlugin().getActiveProfileInterface()).thenReturn(profileInterface); @@ -446,8 +446,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Not configured", smsCommunicatorPlugin.getMessages().get(1).getText()); when(profileInterface.getProfile()).thenReturn(AAPSMocker.getValidProfileStore()); @@ -455,54 +455,54 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE STATUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE STATUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals(AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE STATUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals(AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE LIST smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE LIST"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("1. " + AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE LIST", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("1. " + AAPSMocker.TESTPROFILENAME, smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE 2 (non existing) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 2"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 2", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE 2", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE 1 0(wrong percentage) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE 1 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE 0(wrong index) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("PROFILE 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //PROFILE 1(OK) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To switch profile to someProfile 100% reply with code")); + Assert.assertEquals("PROFILE 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To switch profile to someProfile 100% reply with code")); //PROFILE 1 90(OK) smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "PROFILE 1 90"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("PROFILE 1 90", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To switch profile to someProfile 90% reply with code")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("PROFILE 1 90", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To switch profile to someProfile 90% reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Profile switch created", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Profile switch created", smsCommunicatorPlugin.getMessages().get(3).getText()); } @Test @@ -513,8 +513,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -522,33 +522,33 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //BASAL CANCEL smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL CANCEL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL CANCEL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To stop temp basal reply with code")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("BASAL CANCEL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To stop temp basal reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Temp basal canceled")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("Temp basal canceled")); //BASAL a% smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL a%"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL a%", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL a%", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //BASAL 10% 0 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 10% 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 10% 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL 10% 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyBasalPercentConstraints(any(), any())).thenReturn(new Constraint<>(20)); @@ -556,26 +556,26 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 20% 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 20% 20", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start basal 20% for 20 min reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("BASAL 20% 20", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To start basal 20% for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Temp basal 20% for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Temp basal 20% for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).getText()); //BASAL a smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL a", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL a", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //BASAL 1 0 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 1 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BASAL 1 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyBasalConstraints(any(), any())).thenReturn(new Constraint<>(1d)); @@ -583,12 +583,12 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BASAL 1 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BASAL 1 20", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start basal 1.00U/h for 20 min reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("BASAL 1 20", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To start basal 1.00U/h for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Temp basal 1.00U/h for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Temp basal 1.00U/h for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).getText()); } @@ -600,8 +600,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -609,26 +609,26 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("EXTENDED", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //EXTENDED CANCEL smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED CANCEL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED CANCEL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To stop extended bolus reply with code")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("EXTENDED CANCEL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To stop extended bolus reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Extended bolus canceled")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("Extended bolus canceled")); //EXTENDED a% smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED a%"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED a%", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("EXTENDED a%", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyExtendedBolusConstraints(any())).thenReturn(new Constraint<>(1d)); @@ -636,19 +636,19 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED 1 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED 1 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("EXTENDED 1 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //EXTENDED 1 20 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "EXTENDED 1 20"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("EXTENDED 1 20", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To start extended bolus 1.00U for 20 min reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("EXTENDED 1 20", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To start extended bolus 1.00U for 20 min reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Extended bolus 1.00U for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Extended bolus 1.00U for 20 min started successfully\nVirtual Pump", smsCommunicatorPlugin.getMessages().get(3).getText()); } @Test @@ -659,8 +659,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -668,8 +668,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyBolusConstraints(any())).thenReturn(new Constraint<>(1d)); @@ -678,8 +678,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote bolus not available. Try again later.", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote bolus not available. Try again later.", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyBolusConstraints(any())).thenReturn(new Constraint<>(0d)); @@ -689,15 +689,15 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //BOLUS a smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS a", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS a", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyExtendedBolusConstraints(any())).thenReturn(new Constraint<>(1d)); @@ -707,12 +707,12 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To deliver bolus 1.00U reply with code")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To deliver bolus 1.00U reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.contains("Bolus 1.00U delivered successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().contains("Bolus 1.00U delivered successfully")); //BOLUS 1 (Suspended pump) smsCommunicatorPlugin.setLastRemoteBolusTime(0); @@ -720,27 +720,27 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Pump suspended", smsCommunicatorPlugin.getMessages().get(1).getText()); when(virtualPumpPlugin.isSuspended()).thenReturn(false); //BOLUS 1 a smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1 a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1 a", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("BOLUS 1 a", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //BOLUS 1 MEAL smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "BOLUS 1 MEAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To deliver meal bolus 1.00U reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("BOLUS 1 MEAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To deliver meal bolus 1.00U reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Meal Bolus 1.00U delivered successfully\nVirtual Pump\nTarget 5.0 for 45 minutes", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Meal Bolus 1.00U delivered successfully\nVirtual Pump\nTarget 5.0 for 45 minutes", smsCommunicatorPlugin.getMessages().get(3).getText()); } @Test @@ -751,8 +751,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -760,27 +760,27 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CAL", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); //CAL 0 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CAL 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(XdripCalibrations.sendIntent(any())).thenReturn(true); //CAL 1 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CAL 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CAL 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To send calibration 1.00 reply with code")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("CAL 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To send calibration 1.00 reply with code")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertEquals("Calibration sent. Receiving must be enabled in xDrip.", smsCommunicatorPlugin.getMessages().get(3).text); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertEquals("Calibration sent. Receiving must be enabled in xDrip.", smsCommunicatorPlugin.getMessages().get(3).getText()); } @Test @@ -793,8 +793,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Remote command is not allowed", smsCommunicatorPlugin.getMessages().get(1).getText()); when(SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true); @@ -802,8 +802,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CARBS", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyCarbsConstraints(any())).thenReturn(new Constraint<>(0)); @@ -811,8 +811,8 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 0"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 0", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("CARBS 0", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("Wrong format", smsCommunicatorPlugin.getMessages().get(1).getText()); when(MainApp.getConstraintChecker().applyCarbsConstraints(any())).thenReturn(new Constraint<>(1)); @@ -820,56 +820,56 @@ public class SmsCommunicatorPluginTest { smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 1"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 1", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at")); - String passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("CARBS 1", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To enter 1g at")); + String passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().startsWith("Carbs 1g entered successfully")); //CARBS 1 a smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 1 a"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 1 a", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Wrong format")); + Assert.assertEquals("CARBS 1 a", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("Wrong format")); //CARBS 1 00 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 1 00"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 1 00", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("Wrong format")); + Assert.assertEquals("CARBS 1 00", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("Wrong format")); //CARBS 1 12:01 smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 1 12:01"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 1 12:01", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 12:01PM reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("CARBS 1 12:01", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To enter 1g at 12:01PM reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().startsWith("Carbs 1g entered successfully")); //CARBS 1 3:01AM smsCommunicatorPlugin.setMessages(new ArrayList<>()); sms = new Sms("1234", "CARBS 1 3:01AM"); smsCommunicatorPlugin.processSms(sms); - Assert.assertEquals("CARBS 1 3:01AM", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).text.contains("To enter 1g at 03:01AM reply with code")); - passCode = smsCommunicatorPlugin.getMessageToConfirm().confirmCode; + Assert.assertEquals("CARBS 1 3:01AM", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(1).getText().contains("To enter 1g at 03:01AM reply with code")); + passCode = smsCommunicatorPlugin.getMessageToConfirm().getConfirmCode(); smsCommunicatorPlugin.processSms(new Sms("1234", passCode)); - Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).text); - Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).text.startsWith("Carbs 1g entered successfully")); + Assert.assertEquals(passCode, smsCommunicatorPlugin.getMessages().get(2).getText()); + Assert.assertTrue(smsCommunicatorPlugin.getMessages().get(3).getText().startsWith("Carbs 1g entered successfully")); } @Test public void sendNotificationToAllNumbers() { smsCommunicatorPlugin.setMessages(new ArrayList<>()); smsCommunicatorPlugin.sendNotificationToAllNumbers("abc"); - Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(0).text); - Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(1).text); + Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(0).getText()); + Assert.assertEquals("abc", smsCommunicatorPlugin.getMessages().get(1).getText()); } @Before diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsTest.java index 5f7af11a67..a406a35f9d 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsTest.java @@ -28,19 +28,19 @@ public class SmsTest { when(smsMessage.getMessageBody()).thenReturn("aBody"); Sms sms = new Sms(smsMessage); - Assert.assertEquals(sms.phoneNumber, "aNumber"); - Assert.assertEquals(sms.text, "aBody"); - Assert.assertTrue(sms.received); + Assert.assertEquals(sms.getPhoneNumber(), "aNumber"); + Assert.assertEquals(sms.getText(), "aBody"); + Assert.assertTrue(sms.getReceived()); sms = new Sms("aNumber", "aBody"); - Assert.assertEquals(sms.phoneNumber, "aNumber"); - Assert.assertEquals(sms.text, "aBody"); - Assert.assertTrue(sms.sent); + Assert.assertEquals(sms.getPhoneNumber(), "aNumber"); + Assert.assertEquals(sms.getText(), "aBody"); + Assert.assertTrue(sms.getSent()); sms = new Sms("aNumber", R.string.insulin_unit_shortname); - Assert.assertEquals(sms.phoneNumber, "aNumber"); - Assert.assertEquals(sms.text, MainApp.gs(R.string.insulin_unit_shortname)); - Assert.assertTrue(sms.sent); + Assert.assertEquals(sms.getPhoneNumber(), "aNumber"); + Assert.assertEquals(sms.getText(), MainApp.gs(R.string.insulin_unit_shortname)); + Assert.assertTrue(sms.getSent()); Assert.assertEquals(sms.toString(), "SMS from aNumber: U"); } From e9e1fbae2f139a3077fcd2bac97dd382f440de37 Mon Sep 17 00:00:00 2001 From: dlvoy Date: Tue, 26 Nov 2019 09:14:39 +0100 Subject: [PATCH 42/56] [#2210][#728] - Code coverage config for wear module - Fixed mockito configuration for bug in 2.X --- wear/build.gradle | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/wear/build.gradle b/wear/build.gradle index bb756b2b2c..3a2277e2fe 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -1,4 +1,18 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4' + } +} apply plugin: 'com.android.application' +apply plugin: 'jacoco-android' + +jacoco { + toolVersion = "0.8.3" +} ext { wearableVersion = "2.4.0" @@ -109,7 +123,16 @@ dependencies { testImplementation "junit:junit:4.12" testImplementation "org.json:json:20140107" - testImplementation "org.mockito:mockito-core:2.8.47" + testImplementation ("org.mockito:mockito-core:2.8.47") { + exclude group: 'net.bytebuddy', module: 'byte-buddy' + exclude group: 'net.bytebuddy', module: 'byte-buddy-android' + exclude group: 'net.bytebuddy', module: 'byte-buddy-agent' + } + // to fix org.mockito:mockito-core dependency issues, fixed in mockito 3+ + testImplementation 'net.bytebuddy:byte-buddy:1.8.22' + testImplementation 'net.bytebuddy:byte-buddy-android:1.8.22' + testImplementation 'net.bytebuddy:byte-buddy-agent:1.8.22' + testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" From 20eae8eb1207edd5565d95fd8ee918e65564d515 Mon Sep 17 00:00:00 2001 From: dlvoy Date: Tue, 26 Nov 2019 09:16:46 +0100 Subject: [PATCH 43/56] [#728] Fix for complications update period (from @jotomo review) --- wear/src/main/AndroidManifest.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index 2f89781293..1097be80be 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -246,7 +246,7 @@ android:value="LONG_TEXT" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> + android:value="300" /> Date: Tue, 26 Nov 2019 09:23:26 +0100 Subject: [PATCH 44/56] [#728] Post review / test related minor improvements: - improving mockability when Android classes are used - POJO Pair instead Java - old BGWatchData cleanup fix - Persistence getDataMap fix - minor fixes and improvements to display formating --- .../CobDetailedComplication.java | 2 +- .../IobDetailedComplication.java | 2 +- .../androidaps/data/DisplayRawData.java | 17 +++++--- .../interaction/utils/DisplayFormat.java | 40 +++++++++++------ .../androidaps/interaction/utils/Pair.java | 43 +++++++++++++++++++ .../interaction/utils/Persistence.java | 2 +- .../utils/SmallestDoubleString.java | 9 +++- .../interaction/utils/WearUtil.java | 11 ++++- 8 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/Pair.java diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java index 48341b53ef..ea35b6f45f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java @@ -4,10 +4,10 @@ import android.app.PendingIntent; import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import android.util.Pair; import info.nightscout.androidaps.data.DisplayRawData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; +import info.nightscout.androidaps.interaction.utils.Pair; /* * Created by dlvoy on 2019-11-12 diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java index d9ee729874..790d63aa67 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java @@ -4,10 +4,10 @@ import android.app.PendingIntent; import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import android.util.Pair; import info.nightscout.androidaps.data.DisplayRawData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; +import info.nightscout.androidaps.interaction.utils.Pair; /* * Created by dlvoy on 2019-11-12 diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java b/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java index e9cf997db8..97183cd689 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java @@ -7,6 +7,7 @@ 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; @@ -120,7 +121,7 @@ public class DisplayRawData { public DataMap updateDataFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { Bundle bundle = intent.getBundleExtra("data"); if (bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); + DataMap dataMap = WearUtil.bundleToDataMap(bundle); updateData(dataMap); return dataMap; } @@ -141,7 +142,7 @@ public class DisplayRawData { public DataMap updateStatusFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { Bundle bundle = intent.getBundleExtra("status"); if (bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); + DataMap dataMap = WearUtil.bundleToDataMap(bundle); updateStatus(dataMap); return dataMap; } @@ -168,7 +169,7 @@ public class DisplayRawData { public DataMap updateBasalsFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { Bundle bundle = intent.getBundleExtra("basals"); if (bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); + DataMap dataMap = WearUtil.bundleToDataMap(bundle); updateBasals(dataMap); return dataMap; } @@ -259,10 +260,12 @@ public class DisplayRawData { bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); } - for (int i = 0; i < bgDataList.size(); i++) { - if (bgDataList.get(i).timestamp < (System.currentTimeMillis() - (Constants.HOUR_IN_MS * 5))) { - bgDataList.remove(i); //Get rid of anything more than 5 hours old - break; + // 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/interaction/utils/DisplayFormat.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java index 366ca28eed..4925bb1f5c 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 @@ -1,7 +1,5 @@ package info.nightscout.androidaps.interaction.utils; -import android.util.Pair; - import info.nightscout.androidaps.aaps; import info.nightscout.androidaps.data.DisplayRawData; @@ -45,20 +43,30 @@ public class DisplayFormat { return days + "d"; } else { int weeks = days / 7; - return weeks + "d"; + return weeks + "w"; } } } public static String shortTrend(final DisplayRawData raw) { - String minutes = shortTimeSince(raw.datetime); - String delta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_SHORT_FIELD-1); - - if (minutes.length() + delta.length() + 1 < MAX_SHORT_FIELD) { - delta = deltaSymbol() + delta; + String minutes = "--"; + if (raw.datetime > 0) { + minutes = shortTimeSince(raw.datetime); } - return minutes + " " + delta; + if (minutes.length() + raw.sDelta.length() + deltaSymbol().length() + 1 <= MAX_SHORT_FIELD) { + return minutes + " " + deltaSymbol() + raw.sDelta; + } + + // that only optimizes obvious things like 0 before . or at end, + at beginning + String delta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_SHORT_FIELD-1); + if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_SHORT_FIELD) { + return minutes + " " + deltaSymbol() + delta; + } + + String shortDelta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_SHORT_FIELD-(1+minutes.length())); + + return minutes + " " + shortDelta; } public static String longGlucoseLine(final DisplayRawData raw) { @@ -105,13 +113,17 @@ public class DisplayFormat { final String iob1 = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_SHORT_FIELD); String iob2 = ""; if (raw.sIOB2.contains("|")) { - String[] iobs = raw.sIOB2.replace("(", "").replace(")", "").split("\\|"); - if (iobs.length == 2) { - final String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_IOB_FIELD); - final String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_SHORT_FIELD-1) - Math.max(MIN_IOB_FIELD, iobBolus.length())); - iob2 = iobBolus+" "+iobBasal; + + String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_IOB_FIELD); + if (iobBolus.trim().length() == 0) { + iobBolus = "--"; } + String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_SHORT_FIELD-1) - Math.max(MIN_IOB_FIELD, iobBolus.length())); + if (iobBasal.trim().length() == 0) { + iobBasal = "--"; + } + iob2 = iobBolus+" "+iobBasal; } return Pair.create(iob1, iob2); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Pair.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Pair.java new file mode 100644 index 0000000000..4207cbd743 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Pair.java @@ -0,0 +1,43 @@ +package info.nightscout.androidaps.interaction.utils; + +import java.util.Objects; + +/** + * Same as android Pair, but clean room java class - does not require Android SDK for tests + */ +public class Pair { + + public final F first; + public final S second; + + public Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public static Pair create(F f, S s) { + return new Pair<>(f, s); + } + + @Override + public boolean equals(java.lang.Object o) { + if (o instanceof Pair) { + return ((Pair) o).first.equals(first) && ((Pair) o).second.equals(second); + } else { + return false; + } + } + + @Override + public String toString() { + return "First: \""+first.toString()+"\" Second: \""+second.toString()+"\""; + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + +} + + 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 805aa5aafa..cfc6c2b4b4 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 @@ -23,7 +23,7 @@ public class Persistence { } public DataMap getDataMap(String key) { - if (preferences.contains("raw_data")) { + if (preferences.contains(key)) { final String rawB64Data = preferences.getString(key, null); byte[] rawData = Base64.decode(rawB64Data, Base64.DEFAULT); try { diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java index 5202daf8e4..8361478976 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/SmallestDoubleString.java @@ -71,6 +71,8 @@ public class SmallestDoubleString { } public String minimise(int maxSize) { + final String originalSeparator = separator; + if (Integer.parseInt("0"+fractional) == 0) { separator = ""; fractional = ""; @@ -95,7 +97,7 @@ public class SmallestDoubleString { return toString(); } - if ((fractional.length() > 0)&&(decimal.length() > 0)) { + if (fractional.length() > 0) { int remainingForFraction = maxSize-currentLen()+fractional.length(); String formatCandidate = "#"; if (remainingForFraction>=1) { @@ -104,8 +106,10 @@ public class SmallestDoubleString { DecimalFormat df = new DecimalFormat(formatCandidate); df.setRoundingMode(RoundingMode.HALF_UP); - return sign + df.format(Double.parseDouble(decimal+"."+fractional)).replace(".", separator) + + final String decimalSup = (decimal.length() > 0) ? decimal : "0"; + String result = sign + df.format(Double.parseDouble(decimalSup+"."+fractional)).replace(",", originalSeparator).replace(".", originalSeparator) + ((withUnits == Units.USE) ? units : ""); + return (decimal.length() > 0) ? result : result.substring(1); } return toString(); } @@ -115,6 +119,7 @@ public class SmallestDoubleString { ((withUnits == Units.USE) ? units.length() : 0); } + @Override public String toString() { return sign+decimal+separator+fractional + ((withUnits == Units.USE) ? units : ""); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java index 2a400b4fa0..446d0f7d68 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.interaction.utils; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.PowerManager; import android.util.Log; +import com.google.android.gms.wearable.DataMap; + import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -12,7 +15,6 @@ import java.util.Map; import java.util.Set; import info.nightscout.androidaps.aaps; -import info.nightscout.androidaps.data.DisplayRawData; /** * Created by andy on 3/5/19. @@ -124,4 +126,11 @@ public class WearUtil { } return set; } + + /** + * Taken out to helper method to allow testing + */ + public static DataMap bundleToDataMap(Bundle bundle) { + return DataMap.fromBundle(bundle); + } } From 48aa7a887aac41edaf496c4227913ab9c6276bd2 Mon Sep 17 00:00:00 2001 From: dlvoy Date: Tue, 26 Nov 2019 09:24:41 +0100 Subject: [PATCH 45/56] [#2210][#728] Tests for complications related, formating, perisitence, utils and data-model code of wear module --- .../androidaps/data/BgWatchDataTest.java | 113 ++++++++ .../data/DisplayRawDataBasalsTest.java | 265 ++++++++++++++++++ .../data/DisplayRawDataBgEntriesTest.java | 146 ++++++++++ .../data/DisplayRawDataSgvDataTest.java | 148 ++++++++++ .../data/DisplayRawDataStatusTest.java | 176 ++++++++++++ .../interaction/utils/DisplayFormatTest.java | 204 ++++++++++++++ .../interaction/utils/PairTest.java | 68 +++++ .../interaction/utils/PersistenceTest.java | 189 +++++++++++++ .../interaction/utils/WearUtilTest.java | 186 ++++++++++++ .../testing/mockers/AAPSMocker.java | 63 +++++ .../testing/mockers/AndroidMocker.java | 40 +++ .../androidaps/testing/mockers/LogMocker.java | 11 + .../testing/mockers/RawDataMocker.java | 65 +++++ .../testing/mockers/WearUtilMocker.java | 104 +++++++ .../androidaps/testing/mocks/BundleMock.java | 233 +++++++++++++++ .../androidaps/testing/mocks/IntentMock.java | 39 +++ .../testing/mocks/SharedPreferencesMock.java | 158 +++++++++++ .../testing/utils/BasalWatchDataExt.java | 70 +++++ .../testing/utils/BgWatchDataExt.java | 78 ++++++ .../testing/utils/BolusWatchDataExt.java | 76 +++++ .../testing/utils/TempWatchDataExt.java | 78 ++++++ 21 files changed, 2510 insertions(+) create mode 100644 wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/interaction/utils/PairTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mockers/LogMocker.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mocks/BundleMock.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mocks/IntentMock.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/mocks/SharedPreferencesMock.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java diff --git a/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java new file mode 100644 index 0000000000..fa74d17691 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java @@ -0,0 +1,113 @@ +package info.nightscout.androidaps.data; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.HashSet; +import java.util.Set; + +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; + +import static org.hamcrest.number.OrderingComparison.comparesEqualTo; +import static org.hamcrest.number.OrderingComparison.greaterThan; +import static org.hamcrest.number.OrderingComparison.lessThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class } ) +public class BgWatchDataTest { + + @Before + public void mock() { + WearUtilMocker.prepareMockNoReal(); + } + + @Test + public void bgWatchDataHashTest() { + // GIVEN + BgWatchData inserted = new BgWatchData( + 88.0, 160.0, 90.0, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*4*1, 1 + ); + Set set = new HashSet<>(); + + // THEN + 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); + + assertFalse(item1.equals("aa bbb")); + } + + /** + * 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 + assertThat(item2, lessThan(item1)); + assertThat(item2, greaterThan(item3)); + assertThat(item2, comparesEqualTo(item4)); + } +} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java new file mode 100644 index 0000000000..d3f9dcf48b --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java @@ -0,0 +1,265 @@ +package info.nightscout.androidaps.data; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.ArrayList; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.Persistence; +import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.testing.mockers.AAPSMocker; +import info.nightscout.androidaps.testing.mockers.AndroidMocker; +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; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) +public class DisplayRawDataBasalsTest { + + @Before + public void mock() { + AAPSMocker.prepareMock(); + AAPSMocker.resetMockedSharedPrefs(); + AndroidMocker.mockBase64(); + WearUtilMocker.prepareMockNoReal(); + } + + //============================================================================================== + // 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(DisplayRawData newRaw) { + assertThat(newRaw.tempWatchDataList.size(), is(0)); + assertThat(newRaw.basalWatchDataList.size(), is(0)); + assertThat(newRaw.bolusWatchDataList.size(), is(0)); + assertThat(newRaw.predictionList.size(), is(0)); + } + + private void assertBasalsOk(DisplayRawData newRaw) { + assertThat(newRaw.tempWatchDataList.size(), is(2)); + assertThat(newRaw.basalWatchDataList.size(), is(1)); + assertThat(newRaw.bolusWatchDataList.size(), is(3)); + assertThat(newRaw.predictionList.size(), is(10)); + + assertThat(new TempWatchDataExt(newRaw.tempWatchDataList.get(0)), is(TempWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*20, + 1.5, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*10, + 1.5, + 1.8 + ))); + + assertThat(new TempWatchDataExt(newRaw.tempWatchDataList.get(1)), is(TempWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*10, + 1.3, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*2, + 1.3, + 2.3 + ))); + + assertThat(new BasalWatchDataExt(newRaw.basalWatchDataList.get(0)), is(BasalWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*20, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*2, + 1.2 + ))); + + assertThat(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(0)), is(BolusWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*17, + 5.5, + 20, + false, + true + ))); + + assertThat(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(1)), is(BolusWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*11, + 3, + 0, + false, + true + ))); + + assertThat(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(2)), is(BolusWatchDataExt.build( + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*3, + 0, + 15, + true, + false + ))); + + + assertThat(new BgWatchDataExt(newRaw.predictionList.get(3)), is(BgWatchDataExt.build( + 160-4*3, + WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS*3, + 0 + ))); + + assertThat(new BgWatchDataExt(newRaw.predictionList.get(7)), is(BgWatchDataExt.build( + 160-4*7, + WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS*7, + 0 + ))); + } + + @Test + public void updateBasalsFromEmptyPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateFromPersistence(persistence); + + // THEN + assertBasalsEmpty(newRaw); + } + + @Test + public void updateBasalsFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); + newRaw.updateFromPersistence(persistence); + + // THEN + assertBasalsOk(newRaw); + } + + @Test + public void partialUpdateBasalsFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); + newRaw.partialUpdateFromPersistence(persistence); + + // THEN + assertBasalsEmpty(newRaw); + } + + @Test + public void updateBasalsFromMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + Bundle bundle = BundleMock.mock(dataMapForBasals()); + + intent.putExtra("basals", bundle); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateBasalsFromMessage(intent, null); + + // THEN + assertBasalsOk(newRaw); + } + + @Test + public void updateBasalsFromEmptyMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateBasalsFromMessage(intent, null); + + // THEN + assertBasalsEmpty(newRaw); + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java new file mode 100644 index 0000000000..1908e20dd5 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java @@ -0,0 +1,146 @@ +package info.nightscout.androidaps.data; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.ArrayList; + +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; +import info.nightscout.androidaps.testing.utils.BgWatchDataExt; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class } ) +public class DisplayRawDataBgEntriesTest { + + @Before + public void mock() { + WearUtilMocker.prepareMockNoReal(); + } + + //============================================================================================== + // 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 + DisplayRawData newRaw = new DisplayRawData(); + 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); + assertThat(newRaw.bgDataList.size(), is(12)); + + assertThat(new BgWatchDataExt(newRaw.bgDataList.get(5)), + is(new BgWatchDataExt(new BgWatchData( + 120.0, 170.0, 80.0, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*4*(16-5), 0 + )))); + + assertThat(new BgWatchDataExt(newRaw.bgDataList.get(11)), + is(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); + assertThat(newRaw.bgDataList.size(), is(14)); + + assertThat(new BgWatchDataExt(newRaw.bgDataList.get(12)), + is(new BgWatchDataExt(new BgWatchData( + 92.0, 160.0, 90.0, + WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*4*2, 1 + )))); + assertThat(new BgWatchDataExt(newRaw.bgDataList.get(13)), + is(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); + assertThat(newRaw.bgDataList.size(), is(14)); + } + + @Test + public void addToWatchSetCleanupOldTest() { + DisplayRawData newRaw = new DisplayRawData(); + + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),125)); + assertThat(newRaw.bgDataList.size(), is(1)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*2); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),140)); + assertThat(newRaw.bgDataList.size(), is(2)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*1); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),150)); + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*1 +Constants.MINUTE_IN_MS*30); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),101)); + assertThat(newRaw.bgDataList.size(), is(4)); + + WearUtilMocker.progressClock(Constants.MINUTE_IN_MS*30); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),90)); + assertThat(newRaw.bgDataList.size(), is(5)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*1 +Constants.MINUTE_IN_MS*30); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),80)); + assertThat(newRaw.bgDataList.size(), is(5)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*4); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),92)); + assertThat(newRaw.bgDataList.size(), is(2)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*5 +Constants.MINUTE_IN_MS*30); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),107)); + assertThat(newRaw.bgDataList.size(), is(1)); + + WearUtilMocker.progressClock(Constants.HOUR_IN_MS*6 +Constants.MINUTE_IN_MS*30); + newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp()-Constants.HOUR_IN_MS*6,138)); + assertThat(newRaw.bgDataList.size(), is(0)); + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java new file mode 100644 index 0000000000..55ff9bb815 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java @@ -0,0 +1,148 @@ +package info.nightscout.androidaps.data; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.Persistence; +import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.testing.mockers.AAPSMocker; +import info.nightscout.androidaps.testing.mockers.AndroidMocker; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; +import info.nightscout.androidaps.testing.mocks.BundleMock; +import info.nightscout.androidaps.testing.mocks.IntentMock; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) +public class DisplayRawDataSgvDataTest { + + @Before + public void mock() { + AAPSMocker.prepareMock(); + AAPSMocker.resetMockedSharedPrefs(); + AndroidMocker.mockBase64(); + WearUtilMocker.prepareMockNoReal(); + } + + //============================================================================================== + // 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(DisplayRawData newRaw) { + assertThat(newRaw.sgvLevel, is(0L)); + assertThat(newRaw.datetime, is(0L)); + assertThat(newRaw.sSgv, is("---")); + assertThat(newRaw.sDirection, is("--")); + assertThat(newRaw.sDelta, is("--")); + assertThat(newRaw.sAvgDelta, is("--")); + assertThat(newRaw.sUnits, is("-")); + } + + private void assertDataOk(DisplayRawData newRaw) { + assertThat(newRaw.sgvLevel, is(1L)); + assertThat(newRaw.datetime, is(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS)); + assertThat(newRaw.sSgv, is("106")); + assertThat(newRaw.sDirection, is("↗")); + assertThat(newRaw.sDelta, is("5.4")); + assertThat(newRaw.sAvgDelta, is("3.7")); + assertThat(newRaw.sUnits, is("mg/dl")); + } + + @Test + public void updateDataFromEmptyPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateFromPersistence(persistence); + + // THEN + assertDataEmpty(newRaw); + } + + @Test + public void updateDataFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMapForData()); + newRaw.updateFromPersistence(persistence); + + // THEN + assertDataOk(newRaw); + } + + @Test + public void partialUpdateDataFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMapForData()); + newRaw.partialUpdateFromPersistence(persistence); + + // THEN + assertDataOk(newRaw); + } + + @Test + public void updateDataFromMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + Bundle bundle = BundleMock.mock(dataMapForData()); + + intent.putExtra("data", bundle); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateDataFromMessage(intent, null); + + // THEN + assertDataOk(newRaw); + } + + @Test + public void updateDataFromEmptyMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateDataFromMessage(intent, null); + + // THEN + assertDataEmpty(newRaw); + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java new file mode 100644 index 0000000000..17ac8fd14b --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java @@ -0,0 +1,176 @@ +package info.nightscout.androidaps.data; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.Persistence; +import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.testing.mockers.AAPSMocker; +import info.nightscout.androidaps.testing.mockers.AndroidMocker; +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; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) +public class DisplayRawDataStatusTest { + + @Before + public void mock() { + AAPSMocker.prepareMock(); + AAPSMocker.resetMockedSharedPrefs(); + AndroidMocker.mockBase64(); + WearUtilMocker.prepareMockNoReal(); + } + + @Test + public void toDebugStringTest() { + DisplayRawData raw = RawDataMocker.rawDelta(5, "1.5"); + raw.externalStatusString = "placeholder-here"; + + assertThat(raw.datetime, is(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*5)); + assertThat(raw.toDebugString(), containsString("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(DisplayRawData newRaw) { + assertThat(newRaw.sBasalRate, is("-.--U/h")); + assertThat(newRaw.sUploaderBattery, is("--")); + assertThat(newRaw.sRigBattery, is("--")); + assertThat(newRaw.detailedIOB, is(false)); + assertThat(newRaw.sIOB1, is("IOB")); + assertThat(newRaw.sIOB2, is("-.--")); + assertThat(newRaw.sCOB1, is("Carb")); + assertThat(newRaw.sCOB2, is("--g")); + assertThat(newRaw.sBgi, is("--")); + assertThat(newRaw.showBGI, is(false)); + assertThat(newRaw.externalStatusString, is("no status")); + assertThat(newRaw.batteryLevel, is(1)); + assertThat(newRaw.openApsStatus, is(-1L)); + } + + private void assertStatusOk(DisplayRawData newRaw) { + assertThat(newRaw.sBasalRate, is("120%")); + assertThat(newRaw.sUploaderBattery, is("76")); + assertThat(newRaw.sRigBattery, is("40%")); + assertThat(newRaw.detailedIOB, is(true)); + assertThat(newRaw.sIOB1, is("12.5U")); + assertThat(newRaw.sIOB2, is("(11,2|1,3)")); + assertThat(newRaw.sCOB1, is("Carb")); + assertThat(newRaw.sCOB2, is("5(10)g")); + assertThat(newRaw.sBgi, is("13")); + assertThat(newRaw.showBGI, is(false)); + assertThat(newRaw.externalStatusString, is("")); + assertThat(newRaw.batteryLevel, is(1)); + assertThat(newRaw.openApsStatus, is(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*2)); + } + + @Test + public void updateStatusFromEmptyPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateFromPersistence(persistence); + + // THEN + assertStatusEmpty(newRaw); + } + + @Test + public void updateStatusFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); + newRaw.updateFromPersistence(persistence); + + // THEN + assertStatusOk(newRaw); + } + + @Test + public void partialUpdateStatusFromPersistenceTest() { + // GIVEN + Persistence persistence = new Persistence(); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); + newRaw.partialUpdateFromPersistence(persistence); + + // THEN + assertStatusOk(newRaw); + } + + @Test + public void updateStatusFromMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + Bundle bundle = BundleMock.mock(dataMapForStatus()); + + intent.putExtra("status", bundle); + DisplayRawData newRaw = new DisplayRawData(); + + // WHEN + newRaw.updateStatusFromMessage(intent, null); + + // THEN + assertStatusOk(newRaw); + } + + @Test + public void updateStatusFromEmptyMessageTest() { + // GIVEN + Intent intent = IntentMock.mock(); + DisplayRawData newRaw = new DisplayRawData(); + + // 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 new file mode 100644 index 0000000000..3c838f5898 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java @@ -0,0 +1,204 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.testing.mockers.AAPSMocker; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; + +import static info.nightscout.androidaps.testing.mockers.RawDataMocker.rawCob; +import static info.nightscout.androidaps.testing.mockers.RawDataMocker.rawCobIobBr; +import static info.nightscout.androidaps.testing.mockers.RawDataMocker.rawDelta; +import static info.nightscout.androidaps.testing.mockers.RawDataMocker.rawIob; +import static info.nightscout.androidaps.testing.mockers.RawDataMocker.rawSgv; +import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.backInTime; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * This test covers DisplayFormat class (directly) + * but also SmallestDoubleString - due to carefully chosen input data to format + */ + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class } ) +public class DisplayFormatTest { + + @Before + public void mock() { + WearUtilMocker.prepareMock(); + AAPSMocker.prepareMock(); + AAPSMocker.resetMockedSharedPrefs(); + } + + @Test + public void shortTimeSinceTest() { + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,0,0)), is("0'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,0,5)), is("0'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,0,55)), is("0'")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,1,0)), is("1'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,1,59)), is("1'")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,2,0)), is("2'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,3,0)), is("3'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,4,0)), is("4'")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,10,0)), is("10'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,30,0)), is("30'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,59,0)), is("59'")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,0,59,59)), is("59'")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,1,0,0)), is("1h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,1,30,0)), is("1h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,1,59,59)), is("1h")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,2,0,0)), is("2h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,3,0,0)), is("3h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,4,0,0)), is("4h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,5,0,0)), is("5h")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(0,12,0,0)), is("12h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,18,0,0)), is("18h")); + assertThat(DisplayFormat.shortTimeSince(backInTime(0,23,59,59)), is("23h")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(1,0,0,0)), is("1d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(1,12,0,0)), is("1d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(1,23,59,59)), is("1d")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(2,0,0,0)), is("2d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(3,0,0,0)), is("3d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(4,0,0,0)), is("4d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(5,0,0,0)), is("5d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(6,0,0,0)), is("6d")); + assertThat(DisplayFormat.shortTimeSince(backInTime(6,23,59,59)), is("6d")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(7,0,0,0)), is("1w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(8,0,0,0)), is("1w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(9,0,0,0)), is("1w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(13,23,59,59)), is("1w")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(14,0,0,0)), is("2w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(21,0,0,0)), is("3w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(28,0,0,0)), is("4w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(31,0,0,0)), is("4w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(32,0,0,0)), is("4w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(35,0,0,0)), is("5w")); + + assertThat(DisplayFormat.shortTimeSince(backInTime(100,0,0,0)), is("14w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(200,0,0,0)), is("28w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(365,0,0,0)), is("52w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(366,0,0,0)), is("52w")); + assertThat(DisplayFormat.shortTimeSince(backInTime(367,0,0,0)), is("52w")); + } + + @Test + public void shortTrendTest() { + DisplayRawData raw = new DisplayRawData(); + assertThat(DisplayFormat.shortTrend(raw), is("-- Δ--")); + + raw.datetime = backInTime(0, 0, 2, 0); + assertThat(DisplayFormat.shortTrend(raw), is("2' Δ--")); + + AAPSMocker.setMockedUnicodeComplicationsOn(true); + + // shortening + assertThat(DisplayFormat.shortTrend(rawDelta(2, "1.2")), is("2' Δ1.2")); + assertThat(DisplayFormat.shortTrend(rawDelta(11,"1.2")), is("11' 1.2")); + assertThat(DisplayFormat.shortTrend(rawDelta(12,"0.7")), is("12' Δ.7")); + assertThat(DisplayFormat.shortTrend(rawDelta(10,"1.0")), is("10' Δ1")); + assertThat(DisplayFormat.shortTrend(rawDelta(14,"-5.0")), is("14' Δ-5")); + assertThat(DisplayFormat.shortTrend(rawDelta(13,"-5.1")), is("13' -5")); + assertThat(DisplayFormat.shortTrend(rawDelta(15,"0.87")), is("15' .87")); + assertThat(DisplayFormat.shortTrend(rawDelta(10,"-1.78")), is("10' -2")); + assertThat(DisplayFormat.shortTrend(rawDelta(3, "2.549")), is("3' 2.55")); + assertThat(DisplayFormat.shortTrend(rawDelta(1, "-1.563")), is("1' -1.6")); + + // preserving separator + assertThat(DisplayFormat.shortTrend(rawDelta(2, "1,2")), is("2' Δ1,2")); + assertThat(DisplayFormat.shortTrend(rawDelta(15,"0,87")), is("15' ,87")); + assertThat(DisplayFormat.shortTrend(rawDelta(3, "+2,549")), is("3' 2,55")); + assertThat(DisplayFormat.shortTrend(rawDelta(1, "-1,563")), is("1' -1,6")); + + // UTF-off mode - without delta symbol + AAPSMocker.setMockedUnicodeComplicationsOn(false); + assertThat(DisplayFormat.shortTrend(rawDelta(2, "1.2")), is("2' 1.2")); + assertThat(DisplayFormat.shortTrend(rawDelta(12,"0.7")), is("12' 0.7")); + assertThat(DisplayFormat.shortTrend(rawDelta(10,"1.0")), is("10' 1.0")); + assertThat(DisplayFormat.shortTrend(rawDelta(14,"-5.0")), is("14' -5")); + } + + @Test + public void longGlucoseLine() { + assertThat(DisplayFormat.longGlucoseLine(rawSgv("125",2, "1.2")), is("125→ Δ1.2 (2')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("97",11, "5.2")), is("97↗ Δ5.2 (11')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("110",12,"0.7")), is("110→ Δ.7 (12')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("65",10,"7.0")), is("65↗ Δ7 (10')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("215",14,"-5.0")), is("215↘ Δ-5 (14')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("8.3",13,"-5.1")), is("8.3↘ Δ-5.1 (13')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("6.8",15,"10.83")), is("6.8↑ Δ10.83 (15')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("13.2",10,"-11.78")), is("13.2↓ Δ-11.78 (10')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("3.9",3, "2.549")), is("3.9→ Δ2.549 (3')")); + assertThat(DisplayFormat.longGlucoseLine(rawSgv("11.1",1, "-15.563")), is("11.1↓ Δ-15.563 (1')")); + } + + @Test + public void longDetailsLineTest() { + AAPSMocker.setMockedUnicodeComplicationsOn(true); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("0g", "0U", "3.5U/h")), is("0g ⁞ 0U ⁞ ⎍ 3.5U/h")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("50g", "7.56U", "0%")), is("50g ⁞ 7.56U ⁞ ⎍ 0%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("12g", "3.23U", "120%")), is("12g ⁞ 3.23U ⁞ 120%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("2(40)g", "-1.5U", "0.55U/h")), is("2(40)g ⁞ -2U ⁞ 0.55U/h")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("0(24)g", "0.05U", "160%")), is("0(24)g ⁞ 0.05U ⁞ 160%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("47g", "13.87U", "220%")), is("47g ⁞ 13.87U ⁞ 220%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("13(5)g", "5.90U", "300%")), is("13(5)g ⁞ 5.90U ⁞ 300%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("11(50)g", "0U", "70%")), is("11(50)g ⁞ 0U ⁞ 70%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("7g", "0.54U", "30%")), is("7g ⁞ 0.54U ⁞ ⎍ 30%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("19(38)g", "35.545U", "12.9U/h")), is("19g ⁞ 36U ⁞ 12.9U/h")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("100(1)g", "12.345U", "6.98647U/h")), is("100g 12U 6.98647U/h")); + + AAPSMocker.setMockedUnicodeComplicationsOn(false); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("0g", "0U", "3.5U/h")), is("0g | 0U | 3.5U/h")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("50g", "7.56U", "0%")), is("50g | 7.56U | 0%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("12g", "3.23U", "120%")), is("12g | 3.23U | 120%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("7g", "0.54U", "30%")), is("7g | 0.54U | 30%")); + assertThat(DisplayFormat.longDetailsLine(rawCobIobBr("19(38)g", "35.545U", "12.9U/h")), is("19g | 36U | 12.9U/h")); + } + + @Test + public void detailedIobTest() { + assertThat(DisplayFormat.detailedIob(rawIob("-1.29U", "(0,910|-2,20)")), is(Pair.create("-1.29U", ",91 -2"))); + assertThat(DisplayFormat.detailedIob(rawIob("3.50U", "")), is(Pair.create("3.50U", ""))); + assertThat(DisplayFormat.detailedIob(rawIob("12.5U", "(+1,4|-4.78)")), is(Pair.create("12.5U", "1,4 -5"))); + assertThat(DisplayFormat.detailedIob(rawIob("0.67U", "some junks")), is(Pair.create(".67U", ""))); + assertThat(DisplayFormat.detailedIob(rawIob("-11.0U", "(broken|data)")), is(Pair.create("-11U", "-- --"))); + assertThat(DisplayFormat.detailedIob(rawIob("5.52U", "(0,5439|wrong)")), is(Pair.create("5.52U", ",54 --"))); + assertThat(DisplayFormat.detailedIob(rawIob("-8.1U", "(|-8,1)")), is(Pair.create("-8.1U", "-- -8"))); + assertThat(DisplayFormat.detailedIob(rawIob("-8.1U", "(|-8,1)")), is(Pair.create("-8.1U", "-- -8"))); + assertThat(DisplayFormat.detailedIob(rawIob("7.6U", "(malformed)")), is(Pair.create("7.6U", ""))); + assertThat(DisplayFormat.detailedIob(rawIob("-4.26U", "(6,97|1,3422|too much)")), is(Pair.create("-4.26U", "7 1,3"))); + } + + @Test + public void detailedCobTest() { + assertThat(DisplayFormat.detailedCob(rawCob("0g")), is(Pair.create("0g", ""))); + assertThat(DisplayFormat.detailedCob(rawCob("50g")), is(Pair.create("50g", ""))); + assertThat(DisplayFormat.detailedCob(rawCob("2(40)g")), is(Pair.create("2g", "40g"))); + assertThat(DisplayFormat.detailedCob(rawCob("0(24)g")), is(Pair.create("0g", "24g"))); + assertThat(DisplayFormat.detailedCob(rawCob("13(5)g")), is(Pair.create("13g", "5g"))); + assertThat(DisplayFormat.detailedCob(rawCob("11(50)g")), is(Pair.create("11g", "50g"))); + assertThat(DisplayFormat.detailedCob(rawCob("19(38)g")), is(Pair.create("19g", "38g"))); + assertThat(DisplayFormat.detailedCob(rawCob("100(1)g")), is(Pair.create("100g", "1g"))); + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PairTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PairTest.java new file mode 100644 index 0000000000..ce63330e9c --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PairTest.java @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.interaction.utils; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.HashSet; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +@RunWith(PowerMockRunner.class) +public class PairTest { + + @Test + public void pairEqualsTest() { + // GIVEN + Pair left = Pair.create("aa", "bbb"); + Pair right = Pair.create("ccc", "dd"); + Pair another = Pair.create("aa", "bbb"); + Pair samePos1 = Pair.create("aa", "d"); + Pair samePos2 = Pair.create("zzzzz", "bbb"); + Pair no1 = Pair.create(12, 345L); + Pair no2 = Pair.create(-943, 42); + Pair no3 = Pair.create(12L, 345); + Pair no4 = Pair.create(12, 345L); + + // THEN + assertNotEquals(left, right); + assertEquals(left, another); + assertNotEquals(left, samePos1); + assertNotEquals(left, samePos2); + assertNotEquals(no1, no2); + assertNotEquals(no1, no3); + assertEquals(no1, no4); + + assertFalse(left.equals("aa bbb")); + } + + @Test + public void pairHashTest() { + // GIVEN + Pair inserted = Pair.create("aa", "bbb"); + Set set = new HashSet<>(); + + // THEN + assertFalse(set.contains(inserted)); + set.add(inserted); + assertTrue(set.contains(inserted)); + } + + @Test + public void toStringTest() { + // GIVEN + Pair pair = Pair.create("the-first", "2nd"); + + assertThat(pair.toString(), containsString("the-first")); + assertThat(pair+"", containsString("2nd")); + } + + + +} 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 new file mode 100644 index 0000000000..625216aadc --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java @@ -0,0 +1,189 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.Set; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.testing.mockers.AAPSMocker; +import info.nightscout.androidaps.testing.mockers.AndroidMocker; +import info.nightscout.androidaps.testing.mockers.LogMocker; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; + +import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.REF_NOW; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class} ) +public class PersistenceTest { + + @Before + public void mock() { + WearUtilMocker.prepareMock(); + LogMocker.prepareMock(); + AAPSMocker.prepareMock(); + AAPSMocker.resetMockedSharedPrefs(); + AndroidMocker.mockBase64(); + } + + @Test + public void putStringTest() { + // GIVEN + Persistence persistence = new Persistence(); + + // WHEN + final String emptyGot = persistence.getString("test-key", "default-value"); + persistence.putString("test-key", "newValue"); + final String updatedGot = persistence.getString("test-key", "another-default-value"); + + // THEN + assertThat(emptyGot, is("default-value")); + assertThat(updatedGot, is("newValue")); + } + + @Test + public void putBooleanTest() { + // GIVEN + Persistence persistence = new Persistence(); + + // WHEN + final boolean emptyGot = persistence.getBoolean("test-key", false); + persistence.putBoolean("test-key", true); + final boolean updatedGot = persistence.getBoolean("test-key", false); + + // THEN + assertFalse(emptyGot); + assertTrue(updatedGot); + } + + @Test + public void whenDataUpdatedTest() { + // GIVEN + Persistence persistence = new Persistence(); + DataMap map = new DataMap(); + + // WHEN + final long whenNotUpdated = persistence.whenDataUpdated(); + + Persistence.storeDataMap("data-map", map); + final long whenUpdatedFirst = persistence.whenDataUpdated(); + + WearUtilMocker.progressClock(60000); + Persistence.storeDataMap("data-map", map); + final long whenUpdatedNext = persistence.whenDataUpdated(); + + // THEN + assertThat(0L, is(whenNotUpdated)); + assertThat(REF_NOW, is(whenUpdatedFirst)); + assertThat(REF_NOW + 60000, is(whenUpdatedNext)); + } + + @Test + public void getDataMapTest() { + // GIVEN + Persistence persistence = new Persistence(); + 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"); + byte[] restoredMapContents = restoredMap.getByteArray("test-key"); + + // THEN + assertNull(notExisting); + assertNotNull(restoredMap); + assertTrue(restoredMap.containsKey("test-key")); + + assertThat(restoredMapContents.length, is(4)); + assertThat(restoredMapContents[0], is((byte)9)); + assertThat(restoredMapContents[1], is((byte)42)); + assertThat(restoredMapContents[2], is((byte)127)); + assertThat(restoredMapContents[3], is((byte)-5)); + } + + @Test + public void brokenDataMapTest() { + // GIVEN + Persistence persistence = new Persistence(); + + // WHEN + persistence.putString("data-map", "ZmFrZSBkYXRh"); + DataMap restoredMap = persistence.getDataMap("data-map"); + + // THEN + assertNull(restoredMap); + } + + @Test + public void setsTest() { + // GIVEN + Persistence persistence = new Persistence(); + + // WHEN + Set emptySet = persistence.getSetOf("some fake id"); + + persistence.addToSet("test-set", "element1"); + persistence.addToSet("test-set", "second-elem"); + persistence.addToSet("test-set", "3rd"); + persistence.addToSet("test-set", "czwarty"); + persistence.addToSet("test-set", "V"); + persistence.addToSet("test-set", "6"); + + Set initialSet = persistence.getSetOf("test-set"); + Set sameInitialSet = Persistence.setOf("test-set"); + + persistence.addToSet("test-set", "second-elem"); + persistence.addToSet("test-set", "new-one"); + + Set extendedSet = persistence.getSetOf("test-set"); + + persistence.removeFromSet("test-set", "czwarty"); + persistence.removeFromSet("test-set", "6"); + persistence.removeFromSet("test-set", "3rd"); + + Set reducedSet = persistence.getSetOf("test-set"); + + // THEN + assertThat(emptySet.size(), is(0)); + + assertThat(initialSet.size(), is(6)); + assertTrue(initialSet.contains("element1")); + assertTrue(initialSet.contains("second-elem")); + assertTrue(initialSet.contains("3rd")); + assertTrue(initialSet.contains("czwarty")); + assertTrue(initialSet.contains("V")); + assertTrue(initialSet.contains("6")); + + assertThat(initialSet, is(sameInitialSet)); + + assertThat(extendedSet.size(), is(7)); + assertTrue(extendedSet.contains("new-one")); + + assertThat(reducedSet.size(), is(4)); + assertTrue(reducedSet.contains("element1")); + assertTrue(reducedSet.contains("second-elem")); + assertFalse(reducedSet.contains("3rd")); + assertFalse(reducedSet.contains("czwarty")); + assertTrue(reducedSet.contains("V")); + assertFalse(reducedSet.contains("6")); + assertTrue(reducedSet.contains("new-one")); + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java new file mode 100644 index 0000000000..5bb501584d --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java @@ -0,0 +1,186 @@ +package info.nightscout.androidaps.interaction.utils; + +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gms.wearable.DataMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.HashSet; +import java.util.Set; + +import info.nightscout.androidaps.testing.mockers.LogMocker; +import info.nightscout.androidaps.testing.mockers.WearUtilMocker; +import info.nightscout.androidaps.testing.mocks.BundleMock; + +import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.REF_NOW; +import static org.hamcrest.CoreMatchers.both; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Created by dlvoy on 22.11.2019. + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { WearUtil.class, Log.class} ) +public class WearUtilTest { + + @Before + public void mock() { + WearUtilMocker.prepareMock(); + LogMocker.prepareMock(); + } + + @Test + public void timestampAndTimeDiffsTest() { + + // smoke for mocks - since we freeze "now" to get stable tests + assertThat(REF_NOW, is(WearUtil.timestamp())); + + assertThat(0L, is(WearUtil.msTill(REF_NOW))); + assertThat(3456L, is(WearUtil.msTill(REF_NOW+3456L))); + assertThat(-6294L, is(WearUtil.msTill(REF_NOW-6294L))); + + assertThat(0L, is(WearUtil.msTill(REF_NOW))); + assertThat(-3456L, is(WearUtil.msSince(REF_NOW+3456L))); + assertThat(6294L, is(WearUtil.msSince(REF_NOW-6294L))); + } + + @Test + public void joinSetTest() { + // GIVEN + Set refSet = new HashSet<>(); + refSet.add("element1"); + refSet.add("second-elem"); + refSet.add("3rd"); + + // WHEN + String joined = WearUtil.joinSet(refSet, "|"); + + // THEN + // we cannot guarantee order of items in joined string + // but all items have to be there + assertThat(joined.length(), is("element1".length() + "second-elem".length() + "3rd".length() + "|".length()*2 )); + + assertThat("|"+joined+"|", containsString("|"+"element1"+"|")); + assertThat("|"+joined+"|", containsString("|"+"second-elem"+"|")); + assertThat("|"+joined+"|", containsString("|"+"3rd"+"|")); + } + + @Test + public void explodeSetTest() { + // GIVEN + String serializedSet = "second-elem:element1:3rd"; + + // WHEN + Set set = WearUtil.explodeSet(serializedSet, ":"); + + // THEN + assertThat(set.size(), is(3)); + + assertTrue(set.contains("element1")); + assertTrue(set.contains("second-elem")); + assertTrue(set.contains("3rd")); + } + + @Test + public void explodeSetEmptyElemsTest() { + // GIVEN + String serializedSet = ",,,,real,,,another,,,"; + + // WHEN + Set set = WearUtil.explodeSet(serializedSet, ","); + + // THEN + assertThat(set.size(), is(2)); + + assertThat(true, is(set.contains("real"))); + assertThat(true, is(set.contains("another"))); + } + + @Test + public void joinExplodeStabilityTest() { + // GIVEN + Set refSet = new HashSet<>(); + refSet.add("element1"); + refSet.add("second-elem"); + refSet.add("3rd"); + refSet.add("czwarty"); + refSet.add("V"); + refSet.add("6"); + + // WHEN + String joinedSet = WearUtil.joinSet(refSet, "#"); + final Set explodedSet = WearUtil.explodeSet(joinedSet, "#"); + + // THEN + assertThat(explodedSet, is(refSet)); + } + + @Test + public void threadSleepTest() { + // GIVEN + final long testStart = System.currentTimeMillis(); + final long requestedSleepDuration = 85L; + final long measuringMargin = 100L; + + // WHEN + WearUtil.threadSleep(requestedSleepDuration); + final long measuredSleepDuration = System.currentTimeMillis() - testStart; + + // THEN + // we cannot guarantee to be exact to the millisecond - we add some margin of error + assertThat(measuredSleepDuration, is(both(greaterThan(60L)).and(lessThan(requestedSleepDuration+measuringMargin)))); + } + + @Test + public void rateLimitTest() { + // WHEN + final boolean firstCall = WearUtil.rateLimit("test-limit", 3); + final boolean callAfterward = WearUtil.rateLimit("test-limit", 3); + WearUtilMocker.progressClock(500L); + final boolean callTooSoon = WearUtil.rateLimit("test-limit", 3); + WearUtilMocker.progressClock(3100L); + final boolean callAfterRateLimit = WearUtil.rateLimit("test-limit", 3); + + // THEN + assertTrue(firstCall); + assertFalse(callAfterward); + assertFalse(callTooSoon); + assertTrue(callAfterRateLimit); + } + + /** + * It tests if mock for bundleToDataMap is sane, + * because original impl. of bundleToDataMap + * uses DataMap.fromBundle which need Android SDK runtime + */ + @Test + public void bundleToDataMapTest() { + // GIVEN + DataMap refMap = new DataMap(); + refMap.putString("ala", "ma kota"); + refMap.putInt("why", 42); + refMap.putFloatArray("list", new float[]{0.45f, 3.2f, 6.8f}); + + // WHEN + WearUtilMocker.prepareMockNoReal(); + Bundle bundle = BundleMock.mock(refMap); + DataMap gotMap = WearUtil.bundleToDataMap(bundle); + + // THEN + assertThat(gotMap, is(refMap)); + } + + +} \ No newline at end of file diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java new file mode 100644 index 0000000000..664b431b5c --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java @@ -0,0 +1,63 @@ +package info.nightscout.androidaps.testing.mockers; + +import android.content.Context; +import android.content.SharedPreferences; + +import org.junit.Assert; +import org.mockito.ArgumentMatchers; +import org.mockito.invocation.InvocationOnMock; +import org.powermock.api.mockito.PowerMockito; + +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.aaps; +import info.nightscout.androidaps.testing.mocks.SharedPreferencesMock; + +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +public class AAPSMocker { + + private static final Map mockedSharedPrefs = new HashMap<>(); + private static boolean unicodeComplicationsOn = true; + + public static void prepareMock() { + Context mockedContext = mock(Context.class); + mockStatic(aaps.class, InvocationOnMock::callRealMethod); + try { + PowerMockito.when(aaps.class, "getAppContext").thenReturn(mockedContext); + PowerMockito.when(mockedContext, "getSharedPreferences", ArgumentMatchers.anyString(), ArgumentMatchers.anyInt()).thenAnswer(invocation -> { + + final String key = invocation.getArgument(0); + if (mockedSharedPrefs.containsKey(key)) { + return mockedSharedPrefs.get(key); + } else { + SharedPreferencesMock newPrefs = new SharedPreferencesMock(); + mockedSharedPrefs.put(key, newPrefs); + return newPrefs; + } + }); + PowerMockito.when(aaps.class, "areComplicationsUnicode").thenAnswer(invocation -> unicodeComplicationsOn); + + + } catch (Exception e) { + Assert.fail("Unable to mock objects: " + e.getMessage()); + } + + setMockedUnicodeComplicationsOn(true); + resetMockedSharedPrefs(); + } + + public static void resetMockedSharedPrefs() { + mockedSharedPrefs.clear(); + } + + public static void resetMockedSharedPrefs(String forKey) { + mockedSharedPrefs.remove(forKey); + } + + public static void setMockedUnicodeComplicationsOn(boolean setUnicodeOn) { + unicodeComplicationsOn = setUnicodeOn; + } +} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java new file mode 100644 index 0000000000..62eb5c6301 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java @@ -0,0 +1,40 @@ +package info.nightscout.androidaps.testing.mockers; + +import org.junit.Assert; +import org.powermock.api.mockito.PowerMockito; + +import java.util.Base64; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +public class AndroidMocker { + + public static void mockBase64() { + mockStatic(android.util.Base64.class); + try { + PowerMockito.when(android.util.Base64.class, "decode", anyString(), anyInt()).thenAnswer(invocation -> { + + final String payload = invocation.getArgument(0); + try { + return Base64.getDecoder().decode(payload); + } catch (java.lang.IllegalArgumentException ex) { + return null; + } + }); + + PowerMockito.when(android.util.Base64.class, "encodeToString", any(), anyInt()).thenAnswer(invocation -> { + + final byte[] payload = invocation.getArgument(0); + return Base64.getEncoder().encodeToString(payload); + + }); + + } catch (Exception e) { + Assert.fail("Unable to mock objects: " + e.getMessage()); + } + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/LogMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/LogMocker.java new file mode 100644 index 0000000000..5c374665bc --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/LogMocker.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.testing.mockers; + +import android.util.Log; + +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +public class LogMocker { + public static void prepareMock() { + mockStatic(Log.class); + } +} 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 new file mode 100644 index 0000000000..832720bbff --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java @@ -0,0 +1,65 @@ +package info.nightscout.androidaps.testing.mockers; + +import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.interaction.utils.SafeParse; + +import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.backInTime; + +public class RawDataMocker { + + public static DisplayRawData rawSgv(String sgv, int m, String deltaString) { + DisplayRawData raw = new DisplayRawData(); + raw.datetime = backInTime(0, 0, m, 0); + raw.sDelta = deltaString; + raw.sSgv = sgv; + + double delta = SafeParse.stringToDouble(deltaString); + + if (delta <= (-3.5 * 5)) { + raw.sDirection = "\u21ca"; + } else if (delta <= (-2 * 5)) { + raw.sDirection = "\u2193"; + } else if (delta <= (-1 * 5)) { + raw.sDirection = "\u2198"; + } else if (delta <= (1 * 5)) { + raw.sDirection = "\u2192"; + } else if (delta <= (2 * 5)) { + raw.sDirection = "\u2197"; + } else if (delta <= (3.5 * 5)) { + raw.sDirection = "\u2191"; + } else { + raw.sDirection = "\u21c8"; + } + + return raw; + } + + public static DisplayRawData rawDelta(int m, String delta) { + DisplayRawData raw = new DisplayRawData(); + raw.datetime = backInTime(0, 0, m, 0); + raw.sDelta = delta; + return raw; + } + + public static DisplayRawData rawCobIobBr(String cob, String iob, String br) { + DisplayRawData raw = new DisplayRawData(); + raw.sCOB2 = cob; + raw.sIOB1 = iob; + raw.sBasalRate = br; + return raw; + } + + public static DisplayRawData rawIob(String iob, String iob2) { + DisplayRawData raw = new DisplayRawData(); + raw.sIOB1 = iob; + raw.sIOB2 = iob2; + return raw; + } + + public static DisplayRawData rawCob(String cob) { + DisplayRawData raw = new DisplayRawData(); + raw.sCOB2 = cob; + return raw; + } + +} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java new file mode 100644 index 0000000000..fbe13e52b6 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java @@ -0,0 +1,104 @@ +package info.nightscout.androidaps.testing.mockers; + +import android.os.Bundle; + +import com.google.android.gms.wearable.Asset; +import com.google.android.gms.wearable.DataMap; + +import org.junit.Assert; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; + +import java.util.ArrayList; + +import info.nightscout.androidaps.interaction.utils.Constants; +import info.nightscout.androidaps.interaction.utils.WearUtil; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +public class WearUtilMocker { + + public static final long REF_NOW = 1572610530000L; + private static long clockMsDiff = 0L; + + public static void prepareMock() { + resetClock(); + mockStatic(WearUtil.class, InvocationOnMock::callRealMethod); + try { + // because we cleverly used timestamp() by implementation, we can mock it + // and control the time in tests + PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> (REF_NOW + clockMsDiff)); + } catch (Exception e) { + Assert.fail("Unable to mock the construction of the WearUtil object: " + e.getMessage()); + } + } + + public static void prepareMockNoReal() { + resetClock(); + mockStatic(WearUtil.class); + try { + PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> REF_NOW + clockMsDiff); + PowerMockito.when(WearUtil.class, "getWakeLock", anyString(), anyInt()).then(invocation -> null); + PowerMockito.when(WearUtil.class, "bundleToDataMap", any(Bundle.class)).then(bundleToDataMapMock); + } catch (Exception e) { + Assert.fail("Unable to mock the construction of the WearUtil object: " + e.getMessage()); + } + } + + public static void resetClock() { + clockMsDiff = 0L; + } + + public static void progressClock(long byMilliseconds) { + clockMsDiff = clockMsDiff + byMilliseconds; + } + + public static void setClock(long atMillisecondsSinceEpoch) { + clockMsDiff = atMillisecondsSinceEpoch - REF_NOW; + } + + public static long backInTime(int d, int h, int m, int s) { + return REF_NOW - (Constants.DAY_IN_MS * d + Constants.HOUR_IN_MS * h + Constants.MINUTE_IN_MS * m + Constants.SECOND_IN_MS * s); + } + + private static Answer bundleToDataMapMock = invocation -> { + DataMap map = new DataMap(); + Bundle bundle = invocation.getArgument(0); + for(String key: bundle.keySet()) { + Object v = bundle.get(key); + if (v instanceof Asset) map.putAsset(key, (Asset)v); + if (v instanceof Boolean) map.putBoolean(key, (Boolean)v); + if (v instanceof Byte) map.putByte(key, (Byte)v); + if (v instanceof byte[]) map.putByteArray(key, (byte[])v); + if (v instanceof DataMap) map.putDataMap(key, (DataMap)v); + if (v instanceof Double) map.putDouble(key, (Double)v); + if (v instanceof Float) map.putFloat(key, (Float)v); + if (v instanceof float[]) map.putFloatArray(key, (float[])v); + if (v instanceof Integer) map.putInt(key, (Integer)v); + if (v instanceof Long) map.putLong(key, (Long)v); + if (v instanceof long[]) map.putLongArray(key, (long[])v); + if (v instanceof String) map.putString(key, (String)v); + if (v instanceof String[]) map.putStringArray(key, (String[])v); + + if (v instanceof ArrayList) { + if (!((ArrayList)v).isEmpty()) { + if (((ArrayList) v).get(0) instanceof Integer) { + map.putIntegerArrayList(key, (ArrayList)v); + } + if (((ArrayList) v).get(0) instanceof String) { + map.putStringArrayList(key, (ArrayList)v); + } + if (((ArrayList) v).get(0) instanceof DataMap) { + map.putDataMapArrayList(key, (ArrayList)v); + } + } + } + } + + return map; + }; +} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mocks/BundleMock.java b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/BundleMock.java new file mode 100644 index 0000000000..24449dd352 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/BundleMock.java @@ -0,0 +1,233 @@ +package info.nightscout.androidaps.testing.mocks; + +import android.os.Bundle; +import android.os.Parcelable; +import android.util.SparseArray; + +import com.google.android.gms.wearable.DataMap; + +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyByte; +import static org.mockito.Matchers.anyChar; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyShort; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +public final class BundleMock { + + public static Bundle mock() { + return mock(new HashMap()); + } + + public static Bundle mock(DataMap dataMap) { + HashMap hm = new HashMap<>(); + for (String key : dataMap.keySet()) { + hm.put(key, dataMap.get(key)); + } + return mock(hm); + } + + public static Bundle mock(final HashMap map) { + + Answer unsupported = new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + throw new UnsupportedOperationException(); + } + }; + Answer put = new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + map.put((String)invocation.getArguments()[0], invocation.getArguments()[1]); + return null; + } + }; + Answer get = new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.get(invocation.getArguments()[0]); + } + }; + Answer getOrDefault = new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Object key = invocation.getArguments()[0]; + return map.containsKey(key) ? map.get(key) : invocation.getArguments()[1]; + } + }; + + Bundle bundle = Mockito.mock(Bundle.class); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.size(); + } + }).when(bundle).size(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.isEmpty(); + } + }).when(bundle).isEmpty(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + map.clear(); + return null; + } + }).when(bundle).clear(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.containsKey(invocation.getArguments()[0]); + } + }).when(bundle).containsKey(anyString()); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.get(invocation.getArguments()[0]); + } + }).when(bundle).get(anyString()); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + map.remove(invocation.getArguments()[0]); + return null; + } + }).when(bundle).remove(anyString()); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return map.keySet(); + } + }).when(bundle).keySet(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return BundleMock.class.getSimpleName() + "{map=" + map.toString() + "}"; + } + }).when(bundle).toString(); + + doAnswer(put).when(bundle).putBoolean(anyString(), anyBoolean()); + when(bundle.getBoolean(anyString())).thenAnswer(get); + when(bundle.getBoolean(anyString(), anyBoolean())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putByte(anyString(), anyByte()); + when(bundle.getByte(anyString())).thenAnswer(get); + when(bundle.getByte(anyString(), anyByte())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putChar(anyString(), anyChar()); + when(bundle.getChar(anyString())).thenAnswer(get); + when(bundle.getChar(anyString(), anyChar())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putInt(anyString(), anyShort()); + when(bundle.getShort(anyString())).thenAnswer(get); + when(bundle.getShort(anyString(), anyShort())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putLong(anyString(), anyLong()); + when(bundle.getLong(anyString())).thenAnswer(get); + when(bundle.getLong(anyString(), anyLong())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putFloat(anyString(), anyFloat()); + when(bundle.getFloat(anyString())).thenAnswer(get); + when(bundle.getFloat(anyString(), anyFloat())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putDouble(anyString(), anyDouble()); + when(bundle.getDouble(anyString())).thenAnswer(get); + when(bundle.getDouble(anyString(), anyDouble())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putString(anyString(), anyString()); + when(bundle.getString(anyString())).thenAnswer(get); + when(bundle.getString(anyString(), anyString())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putBooleanArray(anyString(), any(boolean[].class)); + when(bundle.getBooleanArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putLongArray(anyString(), any(long[].class)); + when(bundle.getLongArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putDoubleArray(anyString(), any(double[].class)); + when(bundle.getDoubleArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putIntArray(anyString(), any(int[].class)); + when(bundle.getIntArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putInt(anyString(), anyInt()); + when(bundle.getInt(anyString())).thenAnswer(get); + when(bundle.getInt(anyString(), anyInt())).thenAnswer(getOrDefault); + + doAnswer(unsupported).when(bundle).putAll(any(Bundle.class)); + when(bundle.hasFileDescriptors()).thenAnswer(unsupported); + + doAnswer(put).when(bundle).putShort(anyString(), anyShort()); + when(bundle.getShort(anyString())).thenAnswer(get); + when(bundle.getShort(anyString(), anyShort())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putFloat(anyString(), anyFloat()); + when(bundle.getFloat(anyString())).thenAnswer(get); + when(bundle.getFloat(anyString(), anyFloat())).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putCharSequence(anyString(), any(CharSequence.class)); + when(bundle.getCharSequence(anyString())).thenAnswer(get); + when(bundle.getCharSequence(anyString(), any(CharSequence.class))).thenAnswer(getOrDefault); + + doAnswer(put).when(bundle).putBundle(anyString(), any(Bundle.class)); + when(bundle.getBundle(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putParcelable(anyString(), any(Parcelable.class)); + when(bundle.getParcelable(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putParcelableArray(anyString(), any(Parcelable[].class)); + when(bundle.getParcelableArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putParcelableArrayList(anyString(), any(ArrayList.class)); + when(bundle.getParcelableArrayList(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putSparseParcelableArray(anyString(), any(SparseArray.class)); + when(bundle.getSparseParcelableArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putSerializable(anyString(), any(Serializable.class)); + when(bundle.getSerializable(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putIntegerArrayList(anyString(), any(ArrayList.class)); + when(bundle.getIntegerArrayList(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putStringArrayList(anyString(), any(ArrayList.class)); + when(bundle.getStringArrayList(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putCharSequenceArrayList(anyString(), any(ArrayList.class)); + when(bundle.getCharSequenceArrayList(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putCharArray(anyString(), any(char[].class)); + when(bundle.getCharArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putByteArray(anyString(), any(byte[].class)); + when(bundle.getByteArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putShortArray(anyString(), any(short[].class)); + when(bundle.getShortArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putFloatArray(anyString(), any(float[].class)); + when(bundle.getFloatArray(anyString())).thenAnswer(get); + + doAnswer(put).when(bundle).putCharSequenceArray(anyString(), any(CharSequence[].class)); + when(bundle.getCharSequenceArray(anyString())).thenAnswer(get); + + return bundle; + } +} \ No newline at end of file diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mocks/IntentMock.java b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/IntentMock.java new file mode 100644 index 0000000000..c82a43fed9 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/IntentMock.java @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.testing.mocks; + +import android.content.Intent; +import android.os.Bundle; + +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +public final class IntentMock { + + public static Intent mock() { + return mock(new HashMap()); + } + + public static Intent mock(final HashMap map) { + + Answer put = invocation -> { + map.put((String)invocation.getArguments()[0], invocation.getArguments()[1]); + return null; + }; + Answer get = invocation -> map.get(invocation.getArguments()[0]); + + Intent intent = Mockito.mock(Intent.class); + + when(intent.putExtra(anyString(), any(Bundle.class))).thenAnswer(put); + when(intent.getBundleExtra(anyString())).thenAnswer(get); + + doAnswer(invocation -> map.containsKey(invocation.getArguments()[0])).when(intent).hasExtra(anyString()); + + return intent; + } +} \ No newline at end of file diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mocks/SharedPreferencesMock.java b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/SharedPreferencesMock.java new file mode 100644 index 0000000000..5b0736a450 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mocks/SharedPreferencesMock.java @@ -0,0 +1,158 @@ +package info.nightscout.androidaps.testing.mocks; + +import android.content.SharedPreferences; + +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class SharedPreferencesMock implements SharedPreferences { + + private final EditorInternals editor = new EditorInternals(); + + class EditorInternals implements Editor { + + Map innerMap = new HashMap<>(); + + @Override + public Editor putString(String k, @Nullable String v) { + innerMap.put(k, v); + return this; + } + + @Override + public Editor putStringSet(String k, @Nullable Set set) { + innerMap.put(k, set); + return this; + } + + @Override + public Editor putInt(String k, int i) { + innerMap.put(k, i); + return this; + } + + @Override + public Editor putLong(String k, long l) { + innerMap.put(k, l); + return this; + } + + @Override + public Editor putFloat(String k, float v) { + innerMap.put(k, v); + return this; + } + + @Override + public Editor putBoolean(String k, boolean b) { + innerMap.put(k, b); + return this; + } + + @Override + public Editor remove(String k) { + innerMap.remove(k); + return this; + } + + @Override + public Editor clear() { + innerMap.clear(); + return this; + } + + @Override + public boolean commit() { + return true; + } + + @Override + public void apply() { + + } + } + + @Override + public Map getAll() { + return editor.innerMap; + } + + @Nullable + @Override + public String getString(String k, @Nullable String s) { + if (editor.innerMap.containsKey(k)) { + return (String) editor.innerMap.get(k); + } else { + return s; + } + } + + @Nullable + @Override + public Set getStringSet(String k, @Nullable Set set) { + if (editor.innerMap.containsKey(k)) { + return (Set) editor.innerMap.get(k); + } else { + return set; + } + } + + @Override + public int getInt(String k, int i) { + if (editor.innerMap.containsKey(k)) { + return (Integer) editor.innerMap.get(k); + } else { + return i; + } + } + + @Override + public long getLong(String k, long l) { + if (editor.innerMap.containsKey(k)) { + return (Long) editor.innerMap.get(k); + } else { + return l; + } + } + + @Override + public float getFloat(String k, float v) { + if (editor.innerMap.containsKey(k)) { + return (Float) editor.innerMap.get(k); + } else { + return v; + } + } + + @Override + public boolean getBoolean(String k, boolean b) { + if (editor.innerMap.containsKey(k)) { + return (Boolean) editor.innerMap.get(k); + } else { + return b; + } + } + + @Override + public boolean contains(String k) { + return editor.innerMap.containsKey(k); + } + + @Override + public Editor edit() { + return editor; + } + + @Override + public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + + } + + @Override + public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + + } +} 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 new file mode 100644 index 0000000000..0739e8ac9d --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java @@ -0,0 +1,70 @@ +package info.nightscout.androidaps.testing.utils; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import info.nightscout.androidaps.data.BasalWatchData; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class BasalWatchDataExt extends BasalWatchData { + + private BasalWatchDataExt() { + super(); + } + + public BasalWatchDataExt(BasalWatchData ref) { + super(); + + Set parentFields = new HashSet<>(); + for (Field f : BasalWatchData.class.getDeclaredFields()) { + parentFields.add(f.getName()); + } + + Set knownFields = new HashSet<>(Arrays.asList("startTime,endTime,amount".split(","))); + + // since we do not want modify BasalWatchData - we use this wrapper class + // but we make sure it has same fields + assertThat(parentFields, is(knownFields)); + + 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)||(obj instanceof BasalWatchDataExt)) { + return (this.startTime == ((BasalWatchData) obj).startTime) + && (this.endTime == ((BasalWatchData) obj).endTime) + && (this.amount == ((BasalWatchData) obj).amount); + } else { + return false; + } + } + + @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 new file mode 100644 index 0000000000..0c1d4a0bf3 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java @@ -0,0 +1,78 @@ +package info.nightscout.androidaps.testing.utils; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import info.nightscout.androidaps.data.BgWatchData; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +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(); + + Set parentFields = new HashSet<>(); + for (Field f : BgWatchData.class.getDeclaredFields()) { + parentFields.add(f.getName()); + } + + Set knownFields = new HashSet<>(Arrays.asList("sgv,high,low,timestamp,color".split(","))); + + // since we do not want modify BgWatchDataExt - we use this wrapper class + // but we make sure it has same fields + assertThat(parentFields, is(knownFields)); + + 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)||(obj instanceof BgWatchDataExt)) { + 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 + 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 new file mode 100644 index 0000000000..13e6f52f8f --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java @@ -0,0 +1,76 @@ +package info.nightscout.androidaps.testing.utils; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import info.nightscout.androidaps.data.BolusWatchData; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class BolusWatchDataExt extends BolusWatchData { + + private BolusWatchDataExt() { + super(); + } + + public BolusWatchDataExt(BolusWatchData ref) { + super(); + + Set parentFields = new HashSet<>(); + for (Field f : BolusWatchData.class.getDeclaredFields()) { + parentFields.add(f.getName()); + } + + Set knownFields = new HashSet<>(Arrays.asList("date,bolus,carbs,isSMB,isValid".split(","))); + + // since we do not want modify BolusWatchData - we use this wrapper class + // but we make sure it has same fields + assertThat(parentFields, is(knownFields)); + + 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)||(obj instanceof BolusWatchDataExt)) { + 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; + } + } + + @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/TempWatchDataExt.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java new file mode 100644 index 0000000000..171d8ac841 --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java @@ -0,0 +1,78 @@ +package info.nightscout.androidaps.testing.utils; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import info.nightscout.androidaps.data.TempWatchData; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + + +public class TempWatchDataExt extends TempWatchData { + + private TempWatchDataExt() { + super(); + } + + public TempWatchDataExt(TempWatchData ref) { + super(); + + Set parentFields = new HashSet<>(); + for (Field f : TempWatchData.class.getDeclaredFields()) { + parentFields.add(f.getName()); + } + + Set knownFields = new HashSet<>(Arrays.asList("startTime,startBasal,endTime,endBasal,amount".split(","))); + + // since we do not want modify TempWatchData - we use this wrapper class + // but we make sure it has same fields + assertThat(parentFields, is(knownFields)); + + 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)||(obj instanceof TempWatchDataExt)) { + 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; + } + } + + @Override + public String toString() { + return startTime+", "+startBasal+", "+endTime+", "+endBasal+", "+amount; + } + + @Override + public int hashCode() { + return Objects.hash(startTime, startBasal, endTime, endBasal, amount); + } + +} From 0e370fee028381ba68e339ea89d3a8f01b592bef Mon Sep 17 00:00:00 2001 From: dlvoy Date: Tue, 26 Nov 2019 10:49:01 +0100 Subject: [PATCH 46/56] [#2210][#728] Ignoring JaCoCo runtime-injected fields that affected tests --- .../testing/utils/BasalWatchDataExt.java | 16 ++--------- .../testing/utils/BgWatchDataExt.java | 16 ++--------- .../testing/utils/BolusWatchDataExt.java | 16 ++--------- .../androidaps/testing/utils/ExtUtil.java | 28 +++++++++++++++++++ .../testing/utils/TempWatchDataExt.java | 18 ++---------- 5 files changed, 37 insertions(+), 57 deletions(-) create mode 100644 wear/src/test/java/info/nightscout/androidaps/testing/utils/ExtUtil.java 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 index 0739e8ac9d..9a23f491d9 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java @@ -2,16 +2,11 @@ package info.nightscout.androidaps.testing.utils; import androidx.annotation.Nullable; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import info.nightscout.androidaps.data.BasalWatchData; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; public class BasalWatchDataExt extends BasalWatchData { @@ -22,16 +17,9 @@ public class BasalWatchDataExt extends BasalWatchData { public BasalWatchDataExt(BasalWatchData ref) { super(); - Set parentFields = new HashSet<>(); - for (Field f : BasalWatchData.class.getDeclaredFields()) { - parentFields.add(f.getName()); - } - - Set knownFields = new HashSet<>(Arrays.asList("startTime,endTime,amount".split(","))); - // since we do not want modify BasalWatchData - we use this wrapper class // but we make sure it has same fields - assertThat(parentFields, is(knownFields)); + assertClassHaveSameFields(BasalWatchData.class, "startTime,endTime,amount"); this.startTime = ref.startTime; this.endTime = ref.endTime; 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 index 0c1d4a0bf3..c49125809e 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java @@ -2,16 +2,11 @@ package info.nightscout.androidaps.testing.utils; import androidx.annotation.Nullable; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import info.nightscout.androidaps.data.BgWatchData; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; public class BgWatchDataExt extends BgWatchData { @@ -26,16 +21,9 @@ public class BgWatchDataExt extends BgWatchData { public BgWatchDataExt(BgWatchData ref) { super(); - Set parentFields = new HashSet<>(); - for (Field f : BgWatchData.class.getDeclaredFields()) { - parentFields.add(f.getName()); - } - - Set knownFields = new HashSet<>(Arrays.asList("sgv,high,low,timestamp,color".split(","))); - // since we do not want modify BgWatchDataExt - we use this wrapper class // but we make sure it has same fields - assertThat(parentFields, is(knownFields)); + assertClassHaveSameFields(BgWatchData.class, "sgv,high,low,timestamp,color"); this.sgv = ref.sgv; this.high = ref.high; 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 index 13e6f52f8f..a5553f6a59 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java @@ -2,16 +2,11 @@ package info.nightscout.androidaps.testing.utils; import androidx.annotation.Nullable; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import info.nightscout.androidaps.data.BolusWatchData; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; public class BolusWatchDataExt extends BolusWatchData { @@ -22,16 +17,9 @@ public class BolusWatchDataExt extends BolusWatchData { public BolusWatchDataExt(BolusWatchData ref) { super(); - Set parentFields = new HashSet<>(); - for (Field f : BolusWatchData.class.getDeclaredFields()) { - parentFields.add(f.getName()); - } - - Set knownFields = new HashSet<>(Arrays.asList("date,bolus,carbs,isSMB,isValid".split(","))); - // since we do not want modify BolusWatchData - we use this wrapper class // but we make sure it has same fields - assertThat(parentFields, is(knownFields)); + assertClassHaveSameFields(BolusWatchData.class, "date,bolus,carbs,isSMB,isValid"); this.date = ref.date; this.bolus = ref.bolus; 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 new file mode 100644 index 0000000000..1559db11ae --- /dev/null +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/ExtUtil.java @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.testing.utils; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +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(","))); + assertThat(parentFields, is(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 index 171d8ac841..478c6de966 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java @@ -2,16 +2,11 @@ package info.nightscout.androidaps.testing.utils; import androidx.annotation.Nullable; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import info.nightscout.androidaps.data.TempWatchData; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; public class TempWatchDataExt extends TempWatchData { @@ -23,16 +18,9 @@ public class TempWatchDataExt extends TempWatchData { public TempWatchDataExt(TempWatchData ref) { super(); - Set parentFields = new HashSet<>(); - for (Field f : TempWatchData.class.getDeclaredFields()) { - parentFields.add(f.getName()); - } - - Set knownFields = new HashSet<>(Arrays.asList("startTime,startBasal,endTime,endBasal,amount".split(","))); - - // since we do not want modify TempWatchData - we use this wrapper class + // since we do not want modify BolusWatchData - we use this wrapper class // but we make sure it has same fields - assertThat(parentFields, is(knownFields)); + assertClassHaveSameFields(TempWatchData.class, "startTime,startBasal,endTime,endBasal,amount"); this.startTime = ref.startTime; this.startBasal = ref.startBasal; From f7b556350cf962ed3af6e16f9bb02dfeb61430ca Mon Sep 17 00:00:00 2001 From: dlvoy Date: Wed, 27 Nov 2019 20:32:42 +0100 Subject: [PATCH 47/56] [#2210][#728] Post-review refactoring (thanks @jotomo !) --- .../BaseComplicationProviderService.java | 40 +- .../complications/BrCobIobComplication.java | 14 +- .../CobDetailedComplication.java | 4 +- .../complications/CobIconComplication.java | 4 +- .../complications/CobIobComplication.java | 8 +- .../IobDetailedComplication.java | 4 +- .../complications/IobIconComplication.java | 8 +- .../complications/LongStatusComplication.java | 4 +- .../LongStatusFlippedComplication.java | 5 +- .../complications/SgvComplication.java | 4 +- .../complications/UploaderBattery.java | 4 +- .../complications/WallpaperComplication.java | 5 +- .../androidaps/data/ListenerService.java | 6 +- ...isplayRawData.java => RawDisplayData.java} | 544 +++++++++--------- .../interaction/utils/DisplayFormat.java | 56 +- .../interaction/utils/Inevitable.java | 11 +- .../interaction/utils/Persistence.java | 8 +- .../interaction/utils/WearUtil.java | 14 +- .../androidaps/watchfaces/BaseWatchFace.java | 4 +- .../androidaps/watchfaces/BgGraphBuilder.java | 7 +- .../androidaps/data/BgWatchDataTest.java | 2 +- ...st.java => RawDataSgvDisplayDataTest.java} | 24 +- ...est.java => RawDisplayDataBasalsTest.java} | 24 +- ....java => RawDisplayDataBgEntriesTest.java} | 8 +- ...est.java => RawDisplayDataStatusTest.java} | 26 +- .../interaction/utils/DisplayFormatTest.java | 6 +- .../interaction/utils/PersistenceTest.java | 8 +- .../interaction/utils/WearUtilTest.java | 12 +- .../testing/mockers/AAPSMocker.java | 31 +- .../testing/mockers/AndroidMocker.java | 30 +- .../testing/mockers/RawDataMocker.java | 22 +- .../testing/mockers/WearUtilMocker.java | 26 +- 32 files changed, 477 insertions(+), 496 deletions(-) rename wear/src/main/java/info/nightscout/androidaps/data/{DisplayRawData.java => RawDisplayData.java} (96%) rename wear/src/test/java/info/nightscout/androidaps/data/{DisplayRawDataSgvDataTest.java => RawDataSgvDisplayDataTest.java} (86%) rename wear/src/test/java/info/nightscout/androidaps/data/{DisplayRawDataBasalsTest.java => RawDisplayDataBasalsTest.java} (93%) rename wear/src/test/java/info/nightscout/androidaps/data/{DisplayRawDataBgEntriesTest.java => RawDisplayDataBgEntriesTest.java} (96%) rename wear/src/test/java/info/nightscout/androidaps/data/{DisplayRawDataStatusTest.java => RawDisplayDataStatusTest.java} (88%) 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 c1f33e41c5..49f8f82837 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java @@ -20,7 +20,7 @@ import java.util.Set; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import info.nightscout.androidaps.R; import info.nightscout.androidaps.aaps; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.data.ListenerService; import info.nightscout.androidaps.interaction.utils.Constants; import info.nightscout.androidaps.interaction.utils.DisplayFormat; @@ -38,7 +38,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid private static final String TAG = BaseComplicationProviderService.class.getSimpleName(); private static final String KEY_COMPLICATIONS = "complications"; - private static final String KEY_LAST_SINCE = "lastSince"; + private static final String KEY_LAST_SHOWN_SINCE_VALUE = "lastSince"; private static final String KEY_STALE_REPORTED = "staleReported"; private static final String TASK_ID_REFRESH_COMPLICATION = "refresh-complication"; @@ -56,7 +56,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid // ABSTRACT COMPLICATION INTERFACE //============================================================================================== - public abstract ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent); + public abstract ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent); public abstract String getProviderCanonicalName(); public ComplicationAction getComplicationAction() { return ComplicationAction.MENU; }; @@ -66,11 +66,11 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid //---------------------------------------------------------------------------------------------- public ComplicationData buildNoSyncComplicationData(int dataType, - DisplayRawData raw, + RawDisplayData raw, PendingIntent complicationPendingIntent, PendingIntent exceptionalPendingIntent, long since) { - ComplicationData complicationData = null; + final ComplicationData.Builder builder = new ComplicationData.Builder(dataType); if (dataType != ComplicationData.TYPE_LARGE_IMAGE) { @@ -111,16 +111,14 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid } builder.setTapAction(exceptionalPendingIntent); - complicationData = builder.build(); - return complicationData; + return builder.build(); } public ComplicationData buildOutdatedComplicationData(int dataType, - DisplayRawData raw, + RawDisplayData raw, PendingIntent complicationPendingIntent, PendingIntent exceptionalPendingIntent, long since) { - ComplicationData complicationData = null; final ComplicationData.Builder builder = new ComplicationData.Builder(dataType); if (dataType != ComplicationData.TYPE_LARGE_IMAGE) { @@ -162,8 +160,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid } builder.setTapAction(exceptionalPendingIntent); - complicationData = builder.build(); - return complicationData; + return builder.build(); } /** @@ -230,12 +227,12 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid final Persistence persistence = new Persistence(); - final DisplayRawData raw = new DisplayRawData(); - raw.partialUpdateFromPersistence(persistence); + final RawDisplayData raw = new RawDisplayData(); + raw.updateForComplicationsFromPersistence(persistence); Log.d(TAG, "Complication data: " + raw.toDebugString()); - // store what is currently rendered since field, to detect it need update - persistence.putString(KEY_LAST_SINCE, DisplayFormat.shortTimeSince(raw.datetime)); + // 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)); // by each render we clear stale flag to ensure it is re-rendered at next refresh detection round persistence.putBoolean(KEY_STALE_REPORTED, false); @@ -259,7 +256,6 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid if (complicationData != null) { complicationManager.updateComplicationData(complicationId, complicationData); - } else { // If no data is sent, we still need to inform the ComplicationManager, so the update // job can finish and the wake lock isn't held any longer than necessary. @@ -297,7 +293,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid Log.d(TAG, "Pending check if update needed - "+p.getString(KEY_COMPLICATIONS, "")); Inevitable.task(TASK_ID_REFRESH_COMPLICATION, 15 * Constants.SECOND_IN_MS, () -> { - if (WearUtil.rateLimit("complication-checkIfUpdateNeeded", 5)) { + if (WearUtil.isBelowRateLimit("complication-checkIfUpdateNeeded", 5)) { Log.d(TAG, "Checking if update needed"); requestUpdateIfSinceChanged(); // We reschedule need for check - to make sure next check will Inevitable go in next 15s @@ -314,10 +310,10 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid private static void requestUpdateIfSinceChanged() { final Persistence persistence = new Persistence(); - final DisplayRawData raw = new DisplayRawData(); - raw.partialUpdateFromPersistence(persistence); + final RawDisplayData raw = new RawDisplayData(); + raw.updateForComplicationsFromPersistence(persistence); - final String lastSince = persistence.getString(KEY_LAST_SINCE, "-"); + final String lastSince = persistence.getString(KEY_LAST_SHOWN_SINCE_VALUE, "-"); final String calcSince = DisplayFormat.shortTimeSince(raw.datetime); final boolean isStale = (WearUtil.msSince(persistence.whenDataUpdated()) > Constants.STALE_MS) ||(WearUtil.msSince(raw.datetime) > Constants.STALE_MS); @@ -326,7 +322,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid final boolean sinceWasChanged = !lastSince.equals(calcSince); if (sinceWasChanged|| (isStale && !staleWasRefreshed)) { - persistence.putString(KEY_LAST_SINCE, calcSince); + persistence.putString(KEY_LAST_SHOWN_SINCE_VALUE, calcSince); persistence.putBoolean(KEY_STALE_REPORTED, isStale); Log.d(TAG, "Detected refresh of time needed! Reason: " @@ -351,7 +347,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid Log.d(TAG, "Pending update of "+provider); // We wait with updating allowing all request, from various sources, to arrive Inevitable.task("update-req-"+provider, 700, () -> { - if (WearUtil.rateLimit("update-req-"+provider, 2)) { + if (WearUtil.isBelowRateLimit("update-req-"+provider, 2)) { Log.d(TAG, "Requesting update of "+provider); final ComponentName componentName = new ComponentName(aaps.getAppContext(), provider); final ProviderUpdateRequester providerUpdateRequester = new ProviderUpdateRequester(aaps.getAppContext(), componentName); 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 e9f05c5f79..ec862f4f47 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java @@ -5,13 +5,13 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; -import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; -import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_COB_FIELD; -import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_IOB_FIELD; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_FIELD_LEN_SHORT; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_FIELD_LEN_COB; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MIN_FIELD_LEN_IOB; /* * Created by dlvoy on 2019-11-12 @@ -20,13 +20,13 @@ public class BrCobIobComplication extends BaseComplicationProviderService { private static final String TAG = BrCobIobComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; if (dataType == ComplicationData.TYPE_SHORT_TEXT) { - final String cob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(MIN_COB_FIELD); - final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_IOB_FIELD, (MAX_SHORT_FIELD-1) - cob.length())); + final String cob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(MIN_FIELD_LEN_COB); + final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_IOB, (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)) diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java index ea35b6f45f..350ec9b0ec 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobDetailedComplication.java @@ -5,7 +5,7 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; import info.nightscout.androidaps.interaction.utils.Pair; @@ -16,7 +16,7 @@ public class CobDetailedComplication extends BaseComplicationProviderService { private static final String TAG = CobDetailedComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; 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 40824fd313..e42f46e3a4 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java @@ -7,7 +7,7 @@ import android.support.wearable.complications.ComplicationText; import android.util.Log; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; /* * Created by dlvoy on 2019-11-12 @@ -16,7 +16,7 @@ public class CobIconComplication extends BaseComplicationProviderService { private static final String TAG = CobIconComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; 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 1f7dff7ceb..31ea4dc5f4 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java @@ -5,10 +5,10 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; -import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_FIELD_LEN_SHORT; /* * Created by dlvoy on 2019-11-12 @@ -17,13 +17,13 @@ public class CobIobComplication extends BaseComplicationProviderService { private static final String TAG = CobIobComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { 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(MAX_SHORT_FIELD); + final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(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/IobDetailedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java index 790d63aa67..db1d67a66c 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobDetailedComplication.java @@ -5,7 +5,7 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; import info.nightscout.androidaps.interaction.utils.Pair; @@ -16,7 +16,7 @@ public class IobDetailedComplication extends BaseComplicationProviderService { private static final String TAG = IobDetailedComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; 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 ba253a048d..1dca0f2e4d 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java @@ -7,10 +7,10 @@ import android.support.wearable.complications.ComplicationText; import android.util.Log; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; -import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_SHORT_FIELD; +import static info.nightscout.androidaps.interaction.utils.DisplayFormat.MAX_FIELD_LEN_SHORT; /* * Created by dlvoy on 2019-11-12 @@ -19,12 +19,12 @@ public class IobIconComplication extends BaseComplicationProviderService { private static final String TAG = IobIconComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; if (dataType == ComplicationData.TYPE_SHORT_TEXT) { - final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_SHORT_FIELD); + final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(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/LongStatusComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java index 834f40b0a7..f18ad21662 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusComplication.java @@ -5,7 +5,7 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; /* @@ -15,7 +15,7 @@ public class LongStatusComplication extends BaseComplicationProviderService { private static final String TAG = LongStatusComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java index da3384d358..7c0b730c76 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/LongStatusFlippedComplication.java @@ -5,9 +5,8 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; -import info.nightscout.androidaps.interaction.utils.SmallestDoubleString; /* * Created by dlvoy on 2019-11-12 @@ -16,7 +15,7 @@ public class LongStatusFlippedComplication extends BaseComplicationProviderServi private static final String TAG = LongStatusFlippedComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; 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 b6f9e94946..0296f8bab6 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java @@ -5,7 +5,7 @@ import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; import android.util.Log; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.DisplayFormat; /* @@ -15,7 +15,7 @@ public class SgvComplication extends BaseComplicationProviderService { private static final String TAG = SgvComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java index 4b268ef9c7..a5fcedacdd 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java @@ -8,7 +8,7 @@ import android.util.Log; import androidx.annotation.DrawableRes; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; /* * Created by dlvoy on 2019-11-12 @@ -17,7 +17,7 @@ public class UploaderBattery extends BaseComplicationProviderService { private static final String TAG = UploaderBattery.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java index 2448bebee0..293b1331ca 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/WallpaperComplication.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.io.InputStream; import info.nightscout.androidaps.aaps; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; /* * Created by dlvoy on 2019-11-12 @@ -26,7 +26,7 @@ public abstract class WallpaperComplication extends BaseComplicationProviderServ private static final String TAG = WallpaperComplication.class.getSimpleName(); - public ComplicationData buildComplicationData(int dataType, DisplayRawData raw, PendingIntent complicationPendingIntent) { + public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { ComplicationData complicationData = null; @@ -48,7 +48,6 @@ public abstract class WallpaperComplication extends BaseComplicationProviderServ builder.setLargeImage(Icon.createWithBitmap(scaled)); } catch (IOException e) { Log.e(TAG, "Cannot read wallpaper asset: "+e.getMessage(), e); - e.printStackTrace(); } complicationData = builder.build(); diff --git a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java index 7bd43a79e1..9e018516ba 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java @@ -513,14 +513,14 @@ public class ListenerService extends WearableListenerService implements GoogleAp Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("status", dataMap.toBundle()); - Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMap); + Persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } else if (path.equals(BASAL_DATA_PATH)){ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("basals", dataMap.toBundle()); - Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMap); + Persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } else if (path.equals(NEW_PREFERENCES_PATH)){ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); @@ -544,7 +544,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("data", dataMap.toBundle()); - Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMap); + Persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMap); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } } diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java similarity index 96% rename from wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java rename to wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java index 97183cd689..d4e3580561 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/DisplayRawData.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java @@ -1,272 +1,272 @@ -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 DisplayRawData { - - 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 partialUpdateFromPersistence(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) { - 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"); - } - - 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) { - 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"); - } - - public DataMap updateBasalsFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { - Bundle bundle = intent.getBundleExtra("basals"); - if (bundle != null) { - DataMap dataMap = WearUtil.bundleToDataMap(bundle); - updateBasals(dataMap); - return dataMap; - } - return null; - } - - private void updateBasals(DataMap dataMap) { - WearUtil.getWakeLock("readingPrefs", 500); - loadBasalsAndTemps(dataMap); - } - - 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 - } - } - } -} +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 { + + 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) { + 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"); + } + + 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) { + 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"); + } + + public DataMap updateBasalsFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { + Bundle bundle = intent.getBundleExtra("basals"); + if (bundle != null) { + DataMap dataMap = WearUtil.bundleToDataMap(bundle); + updateBasals(dataMap); + return dataMap; + } + return null; + } + + private void updateBasals(DataMap dataMap) { + WearUtil.getWakeLock("readingPrefs", 500); + loadBasalsAndTemps(dataMap); + } + + 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/interaction/utils/DisplayFormat.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java index 4925bb1f5c..f2cf06671e 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 @@ -1,17 +1,19 @@ package info.nightscout.androidaps.interaction.utils; import info.nightscout.androidaps.aaps; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; public class DisplayFormat { /** - * Maximal lengths of fields/labels shown in complications + * Maximal and minimal lengths of fields/labels shown in complications, in characters + * For MAX values - above that WearOS and watch faces may start ellipsize (...) contents + * For MIN values - this is minimal length that can hold legible data */ - public static final int MAX_LONG_FIELD = 22; // this is empirical, above that many watch faces start to ellipsize - public static final int MAX_SHORT_FIELD = 7; // according to Wear OS docs for TYPE_SHORT_TEXT - public static final int MIN_COB_FIELD = 3; // since carbs are 0..99g - public static final int MIN_IOB_FIELD = 3; // IoB can range from like .1U to 99U + public static final int MAX_FIELD_LEN_LONG = 22; // this is found out empirical, for TYPE_LONG_TEXT + public static final int MAX_FIELD_LEN_SHORT = 7; // according to Wear OS docs for TYPE_SHORT_TEXT + public static final int MIN_FIELD_LEN_COB = 3; // since carbs are usually 0..99g + public static final int MIN_FIELD_LEN_IOB = 3; // IoB can range from like .1U to 99U public static String deltaSymbol() { return aaps.areComplicationsUnicode() ? "\u0394" : ""; @@ -48,32 +50,32 @@ public class DisplayFormat { } } - public static String shortTrend(final DisplayRawData raw) { + public static String shortTrend(final RawDisplayData raw) { String minutes = "--"; if (raw.datetime > 0) { minutes = shortTimeSince(raw.datetime); } - if (minutes.length() + raw.sDelta.length() + deltaSymbol().length() + 1 <= MAX_SHORT_FIELD) { + if (minutes.length() + raw.sDelta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) { return minutes + " " + deltaSymbol() + raw.sDelta; } // that only optimizes obvious things like 0 before . or at end, + at beginning - String delta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_SHORT_FIELD-1); - if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_SHORT_FIELD) { + String delta = (new SmallestDoubleString(raw.sDelta)).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_SHORT_FIELD-(1+minutes.length())); + String shortDelta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_FIELD_LEN_SHORT -(1+minutes.length())); return minutes + " " + shortDelta; } - public static String longGlucoseLine(final DisplayRawData raw) { + public static String longGlucoseLine(final RawDisplayData raw) { return raw.sSgv + raw.sDirection + " " + deltaSymbol() + (new SmallestDoubleString(raw.sDelta)).minimise(8) + " (" + shortTimeSince(raw.datetime) + ")"; } - public static String longDetailsLine(final DisplayRawData raw) { + public static String longDetailsLine(final RawDisplayData raw) { final String SEP_LONG = " " + verticalSeparatorSymbol() + " "; final String SEP_SHORT = " " + verticalSeparatorSymbol() + " "; @@ -81,26 +83,26 @@ public class DisplayFormat { final String SEP_MIN = " "; String line = raw.sCOB2 + SEP_LONG + raw.sIOB1 + SEP_LONG + basalRateSymbol()+raw.sBasalRate; - if (line.length() <= MAX_LONG_FIELD) { + if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } line = raw.sCOB2 + SEP_SHORT + raw.sIOB1 + SEP_SHORT + raw.sBasalRate; - if (line.length() <= MAX_LONG_FIELD) { + if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - int remainingMax = MAX_LONG_FIELD - (raw.sCOB2.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); - final String smallestIoB = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_IOB_FIELD, remainingMax)); + 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; - if (line.length() <= MAX_LONG_FIELD) { + if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - remainingMax = MAX_LONG_FIELD - (smallestIoB.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); - final String simplifiedCob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_COB_FIELD, remainingMax)); + 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)); line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.sBasalRate; - if (line.length() <= MAX_LONG_FIELD) { + if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } @@ -109,17 +111,17 @@ public class DisplayFormat { return line; } - public static Pair detailedIob(DisplayRawData raw) { - final String iob1 = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_SHORT_FIELD); + public static Pair detailedIob(RawDisplayData raw) { + final String iob1 = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_FIELD_LEN_SHORT); String iob2 = ""; if (raw.sIOB2.contains("|")) { String[] iobs = raw.sIOB2.replace("(", "").replace(")", "").split("\\|"); - String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_IOB_FIELD); + String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_FIELD_LEN_IOB); if (iobBolus.trim().length() == 0) { iobBolus = "--"; } - String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_SHORT_FIELD-1) - Math.max(MIN_IOB_FIELD, iobBolus.length())); + String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_FIELD_LEN_SHORT -1) - Math.max(MIN_FIELD_LEN_IOB, iobBolus.length())); if (iobBasal.trim().length() == 0) { iobBasal = "--"; } @@ -128,14 +130,14 @@ public class DisplayFormat { return Pair.create(iob1, iob2); } - public static Pair detailedCob(final DisplayRawData raw) { + public static Pair detailedCob(final RawDisplayData raw) { SmallestDoubleString cobMini = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE); String cob2 = ""; if (cobMini.getExtra().length() > 0) { cob2 = cobMini.getExtra() + cobMini.getUnits(); } - final String cob1 = cobMini.minimise(MAX_SHORT_FIELD); + final String cob1 = cobMini.minimise(MAX_FIELD_LEN_SHORT); return Pair.create(cob1, cob2); } } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java index 21944da9c7..54361b6678 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Inevitable.java @@ -5,6 +5,8 @@ import android.util.Log; import java.util.concurrent.ConcurrentHashMap; +import info.nightscout.androidaps.BuildConfig; + /** * Created for xDrip by jamorham on 07/03/2018 * Adapted for AAPS by dlvoy on 2019-11-11 @@ -18,7 +20,7 @@ public class Inevitable { private static final String TAG = Inevitable.class.getSimpleName(); private static final int MAX_QUEUE_TIME = (int) Constants.MINUTE_IN_MS * 6; - private static final boolean d = true; + private static final boolean debug = BuildConfig.DEBUG; private static final ConcurrentHashMap tasks = new ConcurrentHashMap<>(); @@ -31,14 +33,14 @@ public class Inevitable { // if it already exists then extend the time task.extendTime(idle_for); - if (d) + if (debug) Log.d(TAG, "Extending time for: " + id + " to " + WearUtil.dateTimeText(task.when)); } else { // otherwise create new task if (runnable == null) return; // extension only if already exists tasks.put(id, new Task(id, idle_for, runnable)); - if (d) { + if (debug) { Log.d(TAG, "Creating task: " + id + " due: " + WearUtil.dateTimeText(tasks.get(id).when)); } @@ -58,7 +60,6 @@ public class Inevitable { } }); t.setPriority(Thread.MIN_PRIORITY); - //t.setDaemon(true); t.start(); } } @@ -100,7 +101,7 @@ public class Inevitable { public boolean poll() { final long till = WearUtil.msTill(when); if (till < 1) { - if (d) Log.d(TAG, "Executing task! " + this.id); + if (debug) Log.d(TAG, "Executing task! " + this.id); tasks.remove(this.id); // early remove to allow overlapping scheduling what.run(); return true; 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 cfc6c2b4b4..36c0ae76ee 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,6 +3,8 @@ package info.nightscout.androidaps.interaction.utils; import android.content.SharedPreferences; import android.util.Base64; +import androidx.annotation.Nullable; + import com.google.android.gms.wearable.DataMap; import java.util.Set; @@ -22,15 +24,15 @@ public class Persistence { preferences = aaps.getAppContext().getSharedPreferences(COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY, 0); } + @Nullable public DataMap getDataMap(String key) { if (preferences.contains(key)) { final String rawB64Data = preferences.getString(key, null); byte[] rawData = Base64.decode(rawB64Data, Base64.DEFAULT); try { - DataMap dataMap = DataMap.fromByteArray(rawData); - return dataMap; + return DataMap.fromByteArray(rawData); } catch (IllegalArgumentException ex) { - + // Should never happen, and if it happen - we ignore and fallback to null } } return null; diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java index 446d0f7d68..f8415bdd4a 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.PowerManager; +import android.os.SystemClock; import android.util.Log; import com.google.android.gms.wearable.DataMap; @@ -53,14 +54,14 @@ public class WearUtil { //============================================================================================== // return true if below rate limit - public static synchronized boolean rateLimit(String name, int seconds) { + public static synchronized boolean isBelowRateLimit(String named, int onceForSeconds) { // check if over limit - if ((rateLimits.containsKey(name)) && (timestamp() - rateLimits.get(name) < (seconds * 1000))) { - Log.d(TAG, name + " rate limited: " + seconds + " seconds"); + if ((rateLimits.containsKey(named)) && (timestamp() - rateLimits.get(named) < (onceForSeconds * 1000))) { + Log.d(TAG, named + " rate limited to one for " + onceForSeconds + " seconds"); return false; } // not over limit - rateLimits.put(name, timestamp()); + rateLimits.put(named, timestamp()); return true; } @@ -82,18 +83,15 @@ public class WearUtil { aaps.getAppContext().startActivity(getStartActivityIntent(c)); } - public static Intent getStartActivityIntent(Class c) { return new Intent(aaps.getAppContext(), c).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } - - public static void threadSleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { - // + // we simply ignore if sleep was interrupted } } 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 222f1eda6e..1d1f5758e8 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java @@ -35,7 +35,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import info.nightscout.androidaps.complications.BaseComplicationProviderService; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.data.ListenerService; import info.nightscout.androidaps.R; import lecho.lib.hellocharts.view.LineChartView; @@ -70,7 +70,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferen public LineChartView chart; - public DisplayRawData rawData = new DisplayRawData(); + public RawDisplayData rawData = new RawDisplayData(); public PowerManager.WakeLock wakeLock; // related endTime manual layout 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 b40e15cfd4..ee17d91e76 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java @@ -1,7 +1,6 @@ package info.nightscout.androidaps.watchfaces; import android.content.Context; -import android.graphics.Color; import android.graphics.DashPathEffect; import android.preference.PreferenceManager; import android.text.format.DateFormat; @@ -18,7 +17,7 @@ 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.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.data.TempWatchData; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.AxisValue; @@ -116,7 +115,7 @@ public class BgGraphBuilder { this.end_time = (predictionEndTime>end_time)?predictionEndTime:end_time; } - public BgGraphBuilder(Context context, DisplayRawData raw, int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { + 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, @@ -135,7 +134,7 @@ public class BgGraphBuilder { timespan); } - public BgGraphBuilder(Context context, DisplayRawData raw, int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int 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, diff --git a/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java index fa74d17691..e920dc3202 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java @@ -27,7 +27,7 @@ import static org.junit.Assert.assertTrue; public class BgWatchDataTest { @Before - public void mock() { + public void mock() throws Exception { WearUtilMocker.prepareMockNoReal(); } diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java similarity index 86% rename from wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java rename to wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java index 55ff9bb815..fa73a0272e 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataSgvDataTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java @@ -29,10 +29,10 @@ import static org.junit.Assert.assertThat; @RunWith(PowerMockRunner.class) @PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) -public class DisplayRawDataSgvDataTest { +public class RawDataSgvDisplayDataTest { @Before - public void mock() { + public void mock() throws Exception { AAPSMocker.prepareMock(); AAPSMocker.resetMockedSharedPrefs(); AndroidMocker.mockBase64(); @@ -55,7 +55,7 @@ public class DisplayRawDataSgvDataTest { return dataMap; } - private void assertDataEmpty(DisplayRawData newRaw) { + private void assertDataEmpty(RawDisplayData newRaw) { assertThat(newRaw.sgvLevel, is(0L)); assertThat(newRaw.datetime, is(0L)); assertThat(newRaw.sSgv, is("---")); @@ -65,7 +65,7 @@ public class DisplayRawDataSgvDataTest { assertThat(newRaw.sUnits, is("-")); } - private void assertDataOk(DisplayRawData newRaw) { + private void assertDataOk(RawDisplayData newRaw) { assertThat(newRaw.sgvLevel, is(1L)); assertThat(newRaw.datetime, is(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS)); assertThat(newRaw.sSgv, is("106")); @@ -79,7 +79,7 @@ public class DisplayRawDataSgvDataTest { public void updateDataFromEmptyPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateFromPersistence(persistence); @@ -92,10 +92,10 @@ public class DisplayRawDataSgvDataTest { public void updateDataFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMapForData()); + Persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMapForData()); newRaw.updateFromPersistence(persistence); // THEN @@ -106,11 +106,11 @@ public class DisplayRawDataSgvDataTest { public void partialUpdateDataFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.DATA_PERSISTENCE_KEY, dataMapForData()); - newRaw.partialUpdateFromPersistence(persistence); + Persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMapForData()); + newRaw.updateForComplicationsFromPersistence(persistence); // THEN assertDataOk(newRaw); @@ -123,7 +123,7 @@ public class DisplayRawDataSgvDataTest { Bundle bundle = BundleMock.mock(dataMapForData()); intent.putExtra("data", bundle); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateDataFromMessage(intent, null); @@ -136,7 +136,7 @@ public class DisplayRawDataSgvDataTest { public void updateDataFromEmptyMessageTest() { // GIVEN Intent intent = IntentMock.mock(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateDataFromMessage(intent, null); diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java similarity index 93% rename from wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java rename to wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java index d3f9dcf48b..1245d0fbcb 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBasalsTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java @@ -35,10 +35,10 @@ import static org.junit.Assert.assertThat; @RunWith(PowerMockRunner.class) @PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) -public class DisplayRawDataBasalsTest { +public class RawDisplayDataBasalsTest { @Before - public void mock() { + public void mock() throws Exception { AAPSMocker.prepareMock(); AAPSMocker.resetMockedSharedPrefs(); AndroidMocker.mockBase64(); @@ -119,14 +119,14 @@ public class DisplayRawDataBasalsTest { return dataMap; } - private void assertBasalsEmpty(DisplayRawData newRaw) { + private void assertBasalsEmpty(RawDisplayData newRaw) { assertThat(newRaw.tempWatchDataList.size(), is(0)); assertThat(newRaw.basalWatchDataList.size(), is(0)); assertThat(newRaw.bolusWatchDataList.size(), is(0)); assertThat(newRaw.predictionList.size(), is(0)); } - private void assertBasalsOk(DisplayRawData newRaw) { + private void assertBasalsOk(RawDisplayData newRaw) { assertThat(newRaw.tempWatchDataList.size(), is(2)); assertThat(newRaw.basalWatchDataList.size(), is(1)); assertThat(newRaw.bolusWatchDataList.size(), is(3)); @@ -196,7 +196,7 @@ public class DisplayRawDataBasalsTest { public void updateBasalsFromEmptyPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateFromPersistence(persistence); @@ -209,10 +209,10 @@ public class DisplayRawDataBasalsTest { public void updateBasalsFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); + Persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); newRaw.updateFromPersistence(persistence); // THEN @@ -223,11 +223,11 @@ public class DisplayRawDataBasalsTest { public void partialUpdateBasalsFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); - newRaw.partialUpdateFromPersistence(persistence); + Persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); + newRaw.updateForComplicationsFromPersistence(persistence); // THEN assertBasalsEmpty(newRaw); @@ -240,7 +240,7 @@ public class DisplayRawDataBasalsTest { Bundle bundle = BundleMock.mock(dataMapForBasals()); intent.putExtra("basals", bundle); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateBasalsFromMessage(intent, null); @@ -253,7 +253,7 @@ public class DisplayRawDataBasalsTest { public void updateBasalsFromEmptyMessageTest() { // GIVEN Intent intent = IntentMock.mock(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateBasalsFromMessage(intent, null); diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java similarity index 96% rename from wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java rename to wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java index 1908e20dd5..c200288213 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataBgEntriesTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java @@ -20,10 +20,10 @@ import static org.junit.Assert.assertThat; @RunWith(PowerMockRunner.class) @PrepareForTest( { WearUtil.class } ) -public class DisplayRawDataBgEntriesTest { +public class RawDisplayDataBgEntriesTest { @Before - public void mock() { + public void mock() throws Exception { WearUtilMocker.prepareMockNoReal(); } @@ -62,7 +62,7 @@ public class DisplayRawDataBgEntriesTest { @Test public void addToWatchSetTest() { // GIVEN - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); 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); @@ -107,7 +107,7 @@ public class DisplayRawDataBgEntriesTest { @Test public void addToWatchSetCleanupOldTest() { - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); newRaw.addToWatchSet(dataMapForEntries(WearUtil.timestamp(),125)); assertThat(newRaw.bgDataList.size(), is(1)); diff --git a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java similarity index 88% rename from wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java rename to wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java index 17ac8fd14b..da0daea608 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/DisplayRawDataStatusTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java @@ -31,10 +31,10 @@ import static org.junit.Assert.assertThat; @RunWith(PowerMockRunner.class) @PrepareForTest( { WearUtil.class, Log.class, SharedPreferences.class, Context.class, aaps.class, android.util.Base64.class, Intent.class } ) -public class DisplayRawDataStatusTest { +public class RawDisplayDataStatusTest { @Before - public void mock() { + public void mock() throws Exception { AAPSMocker.prepareMock(); AAPSMocker.resetMockedSharedPrefs(); AndroidMocker.mockBase64(); @@ -43,7 +43,7 @@ public class DisplayRawDataStatusTest { @Test public void toDebugStringTest() { - DisplayRawData raw = RawDataMocker.rawDelta(5, "1.5"); + RawDisplayData raw = RawDataMocker.rawDelta(5, "1.5"); raw.externalStatusString = "placeholder-here"; assertThat(raw.datetime, is(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS*5)); @@ -71,7 +71,7 @@ public class DisplayRawDataStatusTest { return dataMap; } - private void assertStatusEmpty(DisplayRawData newRaw) { + private void assertStatusEmpty(RawDisplayData newRaw) { assertThat(newRaw.sBasalRate, is("-.--U/h")); assertThat(newRaw.sUploaderBattery, is("--")); assertThat(newRaw.sRigBattery, is("--")); @@ -87,7 +87,7 @@ public class DisplayRawDataStatusTest { assertThat(newRaw.openApsStatus, is(-1L)); } - private void assertStatusOk(DisplayRawData newRaw) { + private void assertStatusOk(RawDisplayData newRaw) { assertThat(newRaw.sBasalRate, is("120%")); assertThat(newRaw.sUploaderBattery, is("76")); assertThat(newRaw.sRigBattery, is("40%")); @@ -107,7 +107,7 @@ public class DisplayRawDataStatusTest { public void updateStatusFromEmptyPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateFromPersistence(persistence); @@ -120,10 +120,10 @@ public class DisplayRawDataStatusTest { public void updateStatusFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); + Persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); newRaw.updateFromPersistence(persistence); // THEN @@ -134,11 +134,11 @@ public class DisplayRawDataStatusTest { public void partialUpdateStatusFromPersistenceTest() { // GIVEN Persistence persistence = new Persistence(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN - Persistence.storeDataMap(DisplayRawData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); - newRaw.partialUpdateFromPersistence(persistence); + Persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); + newRaw.updateForComplicationsFromPersistence(persistence); // THEN assertStatusOk(newRaw); @@ -151,7 +151,7 @@ public class DisplayRawDataStatusTest { Bundle bundle = BundleMock.mock(dataMapForStatus()); intent.putExtra("status", bundle); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateStatusFromMessage(intent, null); @@ -164,7 +164,7 @@ public class DisplayRawDataStatusTest { public void updateStatusFromEmptyMessageTest() { // GIVEN Intent intent = IntentMock.mock(); - DisplayRawData newRaw = new DisplayRawData(); + RawDisplayData newRaw = new RawDisplayData(); // WHEN newRaw.updateStatusFromMessage(intent, null); 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 3c838f5898..98fb4fd11b 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 @@ -11,7 +11,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import info.nightscout.androidaps.aaps; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.testing.mockers.AAPSMocker; import info.nightscout.androidaps.testing.mockers.WearUtilMocker; @@ -34,7 +34,7 @@ import static org.junit.Assert.assertThat; public class DisplayFormatTest { @Before - public void mock() { + public void mock() throws Exception { WearUtilMocker.prepareMock(); AAPSMocker.prepareMock(); AAPSMocker.resetMockedSharedPrefs(); @@ -104,7 +104,7 @@ public class DisplayFormatTest { @Test public void shortTrendTest() { - DisplayRawData raw = new DisplayRawData(); + RawDisplayData raw = new RawDisplayData(); assertThat(DisplayFormat.shortTrend(raw), is("-- Δ--")); raw.datetime = backInTime(0, 0, 2, 0); 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 625216aadc..1faa4ddfe7 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 @@ -33,7 +33,7 @@ import static org.junit.Assert.assertTrue; public class PersistenceTest { @Before - public void mock() { + public void mock() throws Exception { WearUtilMocker.prepareMock(); LogMocker.prepareMock(); AAPSMocker.prepareMock(); @@ -88,9 +88,9 @@ public class PersistenceTest { final long whenUpdatedNext = persistence.whenDataUpdated(); // THEN - assertThat(0L, is(whenNotUpdated)); - assertThat(REF_NOW, is(whenUpdatedFirst)); - assertThat(REF_NOW + 60000, is(whenUpdatedNext)); + assertThat(whenNotUpdated, is(0L)); + assertThat(whenUpdatedFirst, is(REF_NOW)); + assertThat(whenUpdatedNext, is(REF_NOW + 60000)); } @Test diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java index 5bb501584d..9e60423c6a 100644 --- a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.java @@ -36,7 +36,7 @@ import static org.junit.Assert.assertTrue; public class WearUtilTest { @Before - public void mock() { + public void mock() throws Exception { WearUtilMocker.prepareMock(); LogMocker.prepareMock(); } @@ -146,12 +146,12 @@ public class WearUtilTest { @Test public void rateLimitTest() { // WHEN - final boolean firstCall = WearUtil.rateLimit("test-limit", 3); - final boolean callAfterward = WearUtil.rateLimit("test-limit", 3); + final boolean firstCall = WearUtil.isBelowRateLimit("test-limit", 3); + final boolean callAfterward = WearUtil.isBelowRateLimit("test-limit", 3); WearUtilMocker.progressClock(500L); - final boolean callTooSoon = WearUtil.rateLimit("test-limit", 3); + final boolean callTooSoon = WearUtil.isBelowRateLimit("test-limit", 3); WearUtilMocker.progressClock(3100L); - final boolean callAfterRateLimit = WearUtil.rateLimit("test-limit", 3); + final boolean callAfterRateLimit = WearUtil.isBelowRateLimit("test-limit", 3); // THEN assertTrue(firstCall); @@ -166,7 +166,7 @@ public class WearUtilTest { * uses DataMap.fromBundle which need Android SDK runtime */ @Test - public void bundleToDataMapTest() { + public void bundleToDataMapTest() throws Exception { // GIVEN DataMap refMap = new DataMap(); refMap.putString("ala", "ma kota"); diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java index 664b431b5c..7684d169f1 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AAPSMocker.java @@ -22,28 +22,23 @@ public class AAPSMocker { private static final Map mockedSharedPrefs = new HashMap<>(); private static boolean unicodeComplicationsOn = true; - public static void prepareMock() { + public static void prepareMock() throws Exception { Context mockedContext = mock(Context.class); mockStatic(aaps.class, InvocationOnMock::callRealMethod); - try { - PowerMockito.when(aaps.class, "getAppContext").thenReturn(mockedContext); - PowerMockito.when(mockedContext, "getSharedPreferences", ArgumentMatchers.anyString(), ArgumentMatchers.anyInt()).thenAnswer(invocation -> { - final String key = invocation.getArgument(0); - if (mockedSharedPrefs.containsKey(key)) { - return mockedSharedPrefs.get(key); - } else { - SharedPreferencesMock newPrefs = new SharedPreferencesMock(); - mockedSharedPrefs.put(key, newPrefs); - return newPrefs; - } - }); - PowerMockito.when(aaps.class, "areComplicationsUnicode").thenAnswer(invocation -> unicodeComplicationsOn); + PowerMockito.when(aaps.class, "getAppContext").thenReturn(mockedContext); + PowerMockito.when(mockedContext, "getSharedPreferences", ArgumentMatchers.anyString(), ArgumentMatchers.anyInt()).thenAnswer(invocation -> { - - } catch (Exception e) { - Assert.fail("Unable to mock objects: " + e.getMessage()); - } + final String key = invocation.getArgument(0); + if (mockedSharedPrefs.containsKey(key)) { + return mockedSharedPrefs.get(key); + } else { + SharedPreferencesMock newPrefs = new SharedPreferencesMock(); + mockedSharedPrefs.put(key, newPrefs); + return newPrefs; + } + }); + PowerMockito.when(aaps.class, "areComplicationsUnicode").thenAnswer(invocation -> unicodeComplicationsOn); setMockedUnicodeComplicationsOn(true); resetMockedSharedPrefs(); diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java index 62eb5c6301..a1addfdc1d 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/AndroidMocker.java @@ -12,29 +12,25 @@ import static org.powermock.api.mockito.PowerMockito.mockStatic; public class AndroidMocker { - public static void mockBase64() { + public static void mockBase64() throws Exception { mockStatic(android.util.Base64.class); - try { - PowerMockito.when(android.util.Base64.class, "decode", anyString(), anyInt()).thenAnswer(invocation -> { - final String payload = invocation.getArgument(0); - try { - return Base64.getDecoder().decode(payload); - } catch (java.lang.IllegalArgumentException ex) { - return null; - } - }); + PowerMockito.when(android.util.Base64.class, "decode", anyString(), anyInt()).thenAnswer(invocation -> { - PowerMockito.when(android.util.Base64.class, "encodeToString", any(), anyInt()).thenAnswer(invocation -> { + final String payload = invocation.getArgument(0); + try { + return Base64.getDecoder().decode(payload); + } catch (java.lang.IllegalArgumentException ex) { + return null; + } + }); - final byte[] payload = invocation.getArgument(0); - return Base64.getEncoder().encodeToString(payload); + PowerMockito.when(android.util.Base64.class, "encodeToString", any(), anyInt()).thenAnswer(invocation -> { - }); + final byte[] payload = invocation.getArgument(0); + return Base64.getEncoder().encodeToString(payload); - } catch (Exception e) { - Assert.fail("Unable to mock objects: " + e.getMessage()); - } + }); } } 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 832720bbff..7d06f00c51 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 @@ -1,14 +1,14 @@ package info.nightscout.androidaps.testing.mockers; -import info.nightscout.androidaps.data.DisplayRawData; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.SafeParse; import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.backInTime; public class RawDataMocker { - public static DisplayRawData rawSgv(String sgv, int m, String deltaString) { - DisplayRawData raw = new DisplayRawData(); + public static RawDisplayData rawSgv(String sgv, int m, String deltaString) { + RawDisplayData raw = new RawDisplayData(); raw.datetime = backInTime(0, 0, m, 0); raw.sDelta = deltaString; raw.sSgv = sgv; @@ -34,30 +34,30 @@ public class RawDataMocker { return raw; } - public static DisplayRawData rawDelta(int m, String delta) { - DisplayRawData raw = new DisplayRawData(); + public static RawDisplayData rawDelta(int m, String delta) { + RawDisplayData raw = new RawDisplayData(); raw.datetime = backInTime(0, 0, m, 0); raw.sDelta = delta; return raw; } - public static DisplayRawData rawCobIobBr(String cob, String iob, String br) { - DisplayRawData raw = new DisplayRawData(); + public static RawDisplayData rawCobIobBr(String cob, String iob, String br) { + RawDisplayData raw = new RawDisplayData(); raw.sCOB2 = cob; raw.sIOB1 = iob; raw.sBasalRate = br; return raw; } - public static DisplayRawData rawIob(String iob, String iob2) { - DisplayRawData raw = new DisplayRawData(); + public static RawDisplayData rawIob(String iob, String iob2) { + RawDisplayData raw = new RawDisplayData(); raw.sIOB1 = iob; raw.sIOB2 = iob2; return raw; } - public static DisplayRawData rawCob(String cob) { - DisplayRawData raw = new DisplayRawData(); + public static RawDisplayData rawCob(String cob) { + RawDisplayData raw = new RawDisplayData(); raw.sCOB2 = cob; return raw; } diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java index fbe13e52b6..11a89f0699 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/WearUtilMocker.java @@ -25,28 +25,22 @@ public class WearUtilMocker { public static final long REF_NOW = 1572610530000L; private static long clockMsDiff = 0L; - public static void prepareMock() { + public static void prepareMock() throws Exception { resetClock(); mockStatic(WearUtil.class, InvocationOnMock::callRealMethod); - try { - // because we cleverly used timestamp() by implementation, we can mock it - // and control the time in tests - PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> (REF_NOW + clockMsDiff)); - } catch (Exception e) { - Assert.fail("Unable to mock the construction of the WearUtil object: " + e.getMessage()); - } + + // because we cleverly used timestamp() by implementation, we can mock it + // and control the time in tests + PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> (REF_NOW + clockMsDiff)); } - public static void prepareMockNoReal() { + public static void prepareMockNoReal() throws Exception { resetClock(); mockStatic(WearUtil.class); - try { - PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> REF_NOW + clockMsDiff); - PowerMockito.when(WearUtil.class, "getWakeLock", anyString(), anyInt()).then(invocation -> null); - PowerMockito.when(WearUtil.class, "bundleToDataMap", any(Bundle.class)).then(bundleToDataMapMock); - } catch (Exception e) { - Assert.fail("Unable to mock the construction of the WearUtil object: " + e.getMessage()); - } + + PowerMockito.when(WearUtil.class, "timestamp").then(invocation -> REF_NOW + clockMsDiff); + PowerMockito.when(WearUtil.class, "getWakeLock", anyString(), anyInt()).then(invocation -> null); + PowerMockito.when(WearUtil.class, "bundleToDataMap", any(Bundle.class)).then(bundleToDataMapMock); } public static void resetClock() { From 58bceb7a6faff8aa12130c10e89fd5eb253dbc23 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 28 Nov 2019 00:56:51 +0100 Subject: [PATCH 48/56] update dependencies --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 31658346b6..f8456a8971 100644 --- a/build.gradle +++ b/build.gradle @@ -8,9 +8,9 @@ buildscript { maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' - classpath 'com.google.gms:google-services:4.3.2' - classpath 'io.fabric.tools:gradle:1.31.0' + classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.google.gms:google-services:4.3.3' + classpath 'io.fabric.tools:gradle:1.31.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 2c8d30ebda61209c58e7ae63514f071adc8c07d6 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 28 Nov 2019 11:05:26 +0100 Subject: [PATCH 49/56] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f22d9cf3a1..4382cc11a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,11 @@ General rules Development guidelines ====================== +Coding convetions +----------------- +1. Use Android Studio with default indents (4 chars, use spaces) +2. Use autoformat feature CTRL-ALR-L in every changed file before commit + Commiting Changes / Pull Requests --------------------------------- From a050a4c5317b59a8fac534093db352ac7e6fb2df Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 28 Nov 2019 11:06:10 +0100 Subject: [PATCH 50/56] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4382cc11a5..d15d2440fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ Development guidelines Coding convetions ----------------- 1. Use Android Studio with default indents (4 chars, use spaces) -2. Use autoformat feature CTRL-ALR-L in every changed file before commit +2. Use autoformat feature CTRL-ALT-L in every changed file before commit Commiting Changes / Pull Requests --------------------------------- From b8ba94fa303a43e402d9a7292ecbfc38e33b3a58 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 28 Nov 2019 17:44:46 +0100 Subject: [PATCH 51/56] New Crowdin translations (#2226) * New translations strings.xml (Italian) * New translations strings.xml (Italian) * New translations strings.xml (Italian) * New translations strings.xml (Italian) * New translations strings.xml (Chinese Simplified) * New translations strings.xml (Swedish) * New translations strings.xml (Bulgarian) * New translations strings.xml (Afrikaans) * New translations strings.xml (Russian) * New translations strings.xml (Lithuanian) * New translations strings.xml (Greek) * New translations strings.xml (German) * New translations strings.xml (French) * New translations strings.xml (Dutch) * New translations strings.xml (Czech) * New translations strings.xml (Korean) * New translations strings.xml (Polish) * New translations strings.xml (Portuguese) * New translations strings.xml (Slovak) * New translations strings.xml (Spanish) * New translations strings.xml (Romanian) * New translations strings.xml (Portuguese, Brazilian) * New translations exam.xml (Italian) * New translations exam.xml (Dutch) * New translations objectives.xml (Italian) * New translations objectives.xml (Romanian) * New translations objectives.xml (Russian) * New translations objectives.xml (Slovak) * New translations objectives.xml (Spanish) * New translations objectives.xml (Swedish) * New translations objectives.xml (Portuguese, Brazilian) * New translations objectives.xml (Polish) * New translations objectives.xml (Lithuanian) * New translations objectives.xml (Greek) * New translations objectives.xml (French) * New translations exam.xml (Swedish) * New translations exam.xml (Spanish) * New translations objectives.xml (Portuguese) * New translations objectives.xml (German) * New translations objectives.xml (Dutch) * New translations objectives.xml (Czech) * New translations objectives.xml (Bulgarian) * New translations insight_alert_titles.xml (Spanish) --- app/src/main/res/values-af-rZA/strings.xml | 2 - app/src/main/res/values-bg-rBG/objectives.xml | 3 +- app/src/main/res/values-bg-rBG/strings.xml | 27 +++ app/src/main/res/values-cs-rCZ/objectives.xml | 2 +- app/src/main/res/values-cs-rCZ/strings.xml | 19 +- app/src/main/res/values-de-rDE/objectives.xml | 2 +- app/src/main/res/values-de-rDE/strings.xml | 19 +- app/src/main/res/values-el-rGR/objectives.xml | 1 - app/src/main/res/values-el-rGR/strings.xml | 2 - app/src/main/res/values-es-rES/exam.xml | 9 +- .../values-es-rES/insight_alert_titles.xml | 2 +- app/src/main/res/values-es-rES/objectives.xml | 1 - app/src/main/res/values-es-rES/strings.xml | 8 +- app/src/main/res/values-fr-rFR/objectives.xml | 2 +- app/src/main/res/values-fr-rFR/strings.xml | 34 ++-- app/src/main/res/values-it-rIT/exam.xml | 4 +- app/src/main/res/values-it-rIT/objectives.xml | 6 +- app/src/main/res/values-it-rIT/strings.xml | 189 ++++++++++-------- app/src/main/res/values-ko-rKR/strings.xml | 2 - app/src/main/res/values-lt-rLT/objectives.xml | 1 - app/src/main/res/values-lt-rLT/strings.xml | 6 +- app/src/main/res/values-nl-rNL/exam.xml | 2 +- app/src/main/res/values-nl-rNL/objectives.xml | 2 +- app/src/main/res/values-nl-rNL/strings.xml | 6 +- app/src/main/res/values-pl-rPL/objectives.xml | 1 - app/src/main/res/values-pl-rPL/strings.xml | 6 +- app/src/main/res/values-pt-rBR/objectives.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 6 + app/src/main/res/values-pt-rPT/objectives.xml | 2 +- app/src/main/res/values-pt-rPT/strings.xml | 2 + app/src/main/res/values-ro-rRO/objectives.xml | 1 - app/src/main/res/values-ro-rRO/strings.xml | 2 - app/src/main/res/values-ru-rRU/objectives.xml | 2 +- app/src/main/res/values-ru-rRU/strings.xml | 27 ++- app/src/main/res/values-sk-rSK/objectives.xml | 2 +- app/src/main/res/values-sk-rSK/strings.xml | 20 ++ app/src/main/res/values-sv-rSE/exam.xml | 3 + app/src/main/res/values-sv-rSE/objectives.xml | 1 - app/src/main/res/values-sv-rSE/strings.xml | 29 +-- app/src/main/res/values-zh-rCN/strings.xml | 2 - 40 files changed, 296 insertions(+), 163 deletions(-) diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml index ef13ded20b..b6f38aa73a 100644 --- a/app/src/main/res/values-af-rZA/strings.xml +++ b/app/src/main/res/values-af-rZA/strings.xml @@ -1138,8 +1138,6 @@ Laai tydelike basale op Laai profiel veranderings, tydelike teikens op Laai BG toetse op - Daglig spaar tyd verandering in 24 h of minder - Daglig spaar tyd het verander minder as 3 ure terug - Geslote lus afgeskakel interne berging beperking Bevry ten minste %1$d MB van intene stoorspasie! Lus gedeaktiveer! Verkeerde formaat diff --git a/app/src/main/res/values-bg-rBG/objectives.xml b/app/src/main/res/values-bg-rBG/objectives.xml index a691b4bff0..677b26f326 100644 --- a/app/src/main/res/values-bg-rBG/objectives.xml +++ b/app/src/main/res/values-bg-rBG/objectives.xml @@ -36,11 +36,12 @@ Отворете съдържанието на Цикъл плъгина. Използвайте функцията за удължаване на периода на графиката, като задържите върху нея. Въведи - Въведете кода, получен от разработчиците да прескочите останалите цели + Ако сте били потребител на OpenAPS и НС има най-малко 3 месеца цикъл на данни, можете да изпращате по електронна поща objectives@androidaps.org с адрес на НС, за да поискате код за заобикаляне на останалите цели. Въведете кода, получен от разработчици Код приет! Неправилен код Докажете знанията си Отговорете правилно на въпросите + Изключено до: %1$s Грешен отговор! Следващия неотговорен Код (request code): %1$s diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index ee3fcd4eea..bf88d217b4 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -98,6 +98,7 @@ Профил Данни за хранене Резултат + Резултат: %1$s %2$s Няма данни за КЗ Не се изисква промяна Искане @@ -215,6 +216,7 @@ Временен базал Удължен болус Nightscout версия: + Нужни %1$dг Настройките са експортирани Експортирай настройките в Импорт на настройки от @@ -258,11 +260,23 @@ Разрешени телефонни номера +XXXXXXXXXX;+YYYYYYYYYY За да доставите болус от %1$.2fЕ отговорете с код %2$s + За да стартирате болус от %1$.2fЕ отговорете с код %2$s + Зa zадаване на временна цел %1$s отговорете с код %2$s + За да спрете временната цел отговорете с код %1$s + За да изключите услугата за отдалечен SMS контрол отговорете с код %1$s.\n\nИмайте предвид, че можете да го активирате само от AAPS смартфона. + Отдалечен SMS контрол е изключен. Можете да го включите от AndroidAPS телефона. За да изпратите калибрация %1$.2f отговорете с код %2$s Болус отказан + Минимален брой минути, в които трябва да мине между един отдалечен болус и следващия + Колко най-малко минути трябва да минат два болуса + За вашата сигурност, променете тази настройка, трябва да добавите най-малко 2 телефонни номера. Болус от %1$.2fЕ доставен успешно Ще стартира %1$.2fЕ болус Болус от %1$.2fЕ доставен успешно + Болус от %1$.2fЕ доставен успешно + Временна цел от %1$s за %2$d минути + Временна цел от %1$s за %2$d минути стартирана успешно + Временна цел успешно спряна Стартирам %1$.2fЕ Позволи отдалечени команди чрез SMS Пръст @@ -322,10 +336,13 @@ За да стартирате базал от %1$.2fЕ/ч за %2$d мин отговорете с код %3$s За да превключите профила към %1$s %2$d%% отговорете с код %3$s За да започнете удължен болус %1$.2fЕ за %2$d мин отговорете с код %3$s + За да въведете %1$dг в %2$s отговорете с код %3$s За да стартирате базал от %1$d%Е/ч за %2$d мин отговорете с код %3$s За да спрете APS за %1$d минути отговорете с код %2$s Временен базал от %1$.2fЕ/ч за %2$d мин стартиран успешно Удължен болус %1$.2fU за %2$d мин стартиран успешно + Въглехидрати %1$dг въведени + Въвеждане на%1$dг въглехидрати - НЕУСПЕШНО Временен базал от %1$d%Е/ч за %2$d мин стартиран успешно Неуспешно стартиране на временен базал Неуспешно стартиране на удължен болус @@ -546,6 +563,8 @@ Показвай подробни статус светлини за канула, инсулин, сензор, резервоар и батерията на началния екран. Ниво за аларма за останал инсулин в резервоара [Е] Критично ниво на останал инсулин в резервоар [Е] + Аларма при заряд на батерия под [%] + Критично ниво на батерията под [%] IOB СОВ Фърмуер @@ -676,6 +695,7 @@ гр. мин. ч. + д ]]> килодж. Ен @@ -704,6 +724,7 @@ Настройки при качване на КЗ към Nightscout Показвай подробна делта Показвай делта с още един десетичен знак + Максимум минути СМБ Максимални минути за ограничаване на базала от SMB Неподдържан фърмуер на помпата Изпращай данни за КЗ към xDrip+ @@ -1232,6 +1253,8 @@ Не е избрано (общ) Алкални Литиева + NiZn батерия + Болус/Корекция подробно СКАНИРАЙ СТОП @@ -1343,6 +1366,7 @@ Смени профила на Смени профила на %1$s Последно свързване към помпа + Последнa връзкa с помпата [минути] Последна връзка с помпата е %1$s %2$s минути преди Изпращане на SMS с текст %1$s Изпрати SMS до всички телефони от настройките @@ -1367,4 +1391,7 @@ %1$d %% Болус калкулатор мин + %1$dг + Вкл + Изкл diff --git a/app/src/main/res/values-cs-rCZ/objectives.xml b/app/src/main/res/values-cs-rCZ/objectives.xml index 518ea0965c..259403ad81 100644 --- a/app/src/main/res/values-cs-rCZ/objectives.xml +++ b/app/src/main/res/values-cs-rCZ/objectives.xml @@ -36,7 +36,7 @@ Zobrazte obsah modulu Smyčka Přepněte režim zobrazení dlouhým stisknutím grafu glykémie Zadat - Zadejte kód, který jste získali od vývojářů, abyste obešli zbývající cíle + Pokud jste byli dříve uživateli OpenAPS a váš NS má alespoň 3 měsíce dat z používání smyčky, můžete poslat e-mail na adresu objectives@androidaps.org s adresou svého NS a požádat o kód, pomocí něhož budete moci obejít zbývající cíle. Zadejte kód získaný od vývojářů. Kód přijat Neplatný kód Prokažte své znalosti diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 1b72aaf6a5..2e524d4586 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -260,11 +260,23 @@ Povolená tel. čísla +XXXXXXXXXX;+YYYYYYYYYY K potvrzení bolusu %1$.2fU odpověz SMS s kódem %2$s + Pro potvrzení bolusu na jídlo %1$.2fU odpovězte pomocí SMS s kódem %2$s + Pro nastavení dočasného cíle %1$s odpovězte pomocí SMS s kódem %2$s + Pro zrušení dočasného cíle odpovězte pomocí SMS s kódem %1$s + Chcete-li deaktivovat Vzdálené řízení přes SMS, odpovězte pomocí SMS s kódem %1$s.\n\nUpozornění: tuto funkci budete moci znovu aktivovat pouze z telefonu s hlavní verzí AAPS. + Služba Vzdáleného řízení přes SMS zastavena. Chcete-li ji znovu aktivovat, použijte telefon s hlavní verzí AAPS. Odeslání kalibrace %1$.2f potvrďte kódem %2$s Chyba při aplikování bolusu + Minimální počet minut, které musí uplynout mezi dvěma bolusy podanými přes vzdálené řízení + Kolik minut (minimálně) musí uplynout mezi dvěma bolusy + Úprava tohoto nastavení v zájmu vaší bezpečnosti vyžaduje, abyste zadali alespoň 2 telefonní čísla. Bolus %1$.2fU aplikován úspěšně Podávání %1$.2fU inzulínu Bolus %1$.2fU aplikován úspěšně + Bolus na jídlo %1$.2fU byl úspěšně aplikován + Cíl %1$s na %2$d minut + Cíl %1$s na %2$d minut byl úspěšně nastaven + Dočasný cíl byl úspěšně zrušen Aplikováno %1$.2fU Povolit posílání příkazů přes SMS Glukoměr @@ -324,10 +336,13 @@ Pro spuštění bazálu %1$.2fU/h na %2$d min odpovězte SMS s kódem %3$s Pro přepnutí profilu na %1$s %2$d%% odpovězte SMS s kódem %3$s Pro spuštění prodlouženého bolusu %1$.2fU na %2$d min odpovězte SMS s kódem %3$s + Pro zadání %1$dg na %2$s odpovězte pomocí SMS s kódem %3$s Pro spuštění bazálu %1$d%% na %2$d min odpovězte SMS s kódem %3$s K pozastavení smyčky na %1$d minut odpověz SMS s kódem %2$s Dočasný bazál %1$.2fU/h na %2$d minut spuštěn Prodloužený bolus %1$.2fU na %2$d min úspěšně spuštěn + Sacharidy %1$dg byly úspěšně zadány + Zadání %1$dg sacharidů se nezdařilo Dočasný bazál %1$d%% na %2$d minut úspěšně spuštěn Spuštění dočasného bazálu selhalo Spuštění prodlouženého bolusu selhalo @@ -680,6 +695,7 @@ g m h + d ]]> kJ En @@ -708,6 +724,7 @@ Nastavení nahrávání glykémií Zobrazovat detailní změny Zobrazovat rozdíl s jedním desetinným místem navíc + Max. minut SMB Maximální počet minut bazálu, ke kterým se limituje SMB Nepodporovaný firmware v pumpě Odesílat data do xDrip+ @@ -1148,7 +1165,7 @@ Nahrávat přepnutí profilu, dočasné cíle Nahrávat měření z prstu Změna letního času za méně než 24 hodin - Změna letního času za méně než 3 hodiny - Uzavřená smyčka zastavena + Změna letního času za méně než 3 hodiny – Uzavřená smyčka zastavena omezení vnitřního úložiště Uvolněte alespoň %1$d MB z vnitřního úložiště! Smyčka zakázána! Chybný formát diff --git a/app/src/main/res/values-de-rDE/objectives.xml b/app/src/main/res/values-de-rDE/objectives.xml index e92e116e1a..1158cee370 100644 --- a/app/src/main/res/values-de-rDE/objectives.xml +++ b/app/src/main/res/values-de-rDE/objectives.xml @@ -37,7 +37,7 @@ die Formel maxIOB = durchschnittlicher Essensbolus + 3 x höchste BasalrateZeige den Inhalt des Loop-Plugins an Nutze die Skalierfunktion. Drücke dazu lange auf das BZ-Diagramm OK - Gib den Code ein, den Du von den Entwicklern erhalten hast, um die restlichen Objectives zu überspringen. + Wenn Du früher bereits OpenAPS genutzt hast und auf Deiner Nightscout-Seite mindestens drei Monate Closed-Loop-Daten ersichtlich sind, kannst Du eine E-Mail an objectives@androidaps.org mit Deiner NS-URL und Deinem Anforderungscode senden, um die restlichen Ziele zu überspringen. Code eingeben, der von den Entwicklern übermittelt wurde: Code akzeptiert Code ungültig Prüfe Dein Wissen diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index b9f17dd119..b8b04fbce0 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -260,11 +260,23 @@ Erlaubte Telefonnummern +XXXXXXXXXX;+YYYYYYYYYY Um einen Bolus von %1$.2f IE abzugeben, antworte mit dem Code %2$s. + Um einen Mahlzeitenbolus von %1$.2f IE abzugeben, antworte mit dem Code %2$s. + Um ein Temp Target von %1$s zu setzen, antworte mit dem Code %2$s + Um das temporäre Ziel zu stoppen, antworte mit dem Code %1$s + Um den SMS Remote Service zu deaktivieren, antworte mit dem Code %1$s.\n\nBeachte, dass Du diesen nur am AAPS Master Smartphone wieder aktivieren kannst. + SMS Remote Service gestoppt. Verwende das AAPS Master, um ihn wieder zu aktivieren. Um die Kalibrierung %1$.2f zu senden, antworte mit dem Code %2$s. Bolus fehlgeschlagen + Minimale Dauer in Minuten, die nach einem Remote Bolus verstrichen sein muss, bevor ein neuer abgegeben werden kann. + Anzahl Minuten, die mindestens zwischen zwei Bolusgaben liegen müssen. + Aus Sicherheitsgründen musst Du mindestens zwei Telefonnummern eintragen, um diese Voreinstellung zu ändern. Bolus %1$.2fIE erfolgreich abgegeben Werde %1$.2fIE abgeben Bolus %1$.2fIE erfolgreich abgegeben + Mahlzeiten-Bolus %1$.2f IE erfolgreich abgegeben. + Ziel %1$s für %2$d Minuten + Ziel %1$s für %2$d Minuten erfolgreich gesetzt. + Temporäres Ziel wurde erfolgreich abgebrochen Gebe %1$.2fIE ab Erlaube externe Befehle per SMS Finger @@ -314,7 +326,7 @@ Limit erreicht Kein Profil ausgewählt Loop wurde deaktiviert. - Lopp wurde aktiviert + Loop wurde aktiviert Loop ist deaktiviert. Loop ist aktiviert. %1$.2f limitiert auf %2$.2f @@ -324,10 +336,13 @@ Um eine Basalrate von %1$.2fIE/h für %2$d Minuten zu setzen, antworte mit dem Code %3$s Um das Profil auf %1$s %2$d%% zu setzen, antworte mit dem Code %3$s Um den erweiterten Bolus %1$.2fIE für %2$d Minuten abzugeben, antworte mit dem Code %3$s + Um %1$dg Kohlenhydrate um %2$s einzugeben, antworte mit dem Code %3$s Um die Basalrate von %1$d%% für %2$d Minuten zu setzen, antworte mit dem Code %3$s Um das Loopen für %1$d Minuten zu pausieren, antworte mit dem Code %2$s. TBR mit %1$.2f IE/h für %2$d min wurde erfolgreich gestartet. Der erweiterte Bolus %1$.2f IE/h für %2$d Minuten wurde erfolgreich gestartet + %1$dg Kohlenhydrate erfolgreich erfasst + Eingabe von %1$dg Kohlenhydraten ist fehlgeschlagen. Die temporäre Basalrate wurde erfolgreich für %2$d Minuten auf %1$d%% gesetzt Das Starten der TBR ist fehlgeschlagen. Die Abgabe des erweiterten Bolus ist fehlgeschlagen @@ -680,6 +695,7 @@ g min h + T ]]> kJ En @@ -708,6 +724,7 @@ BZ Upload Einstellungen Zeige detailliertes Delta Delta wird mit Dezimalstelle angezeigt. + SMB max. Minuten SMB Basal-Limit in Minuten Nicht unterstützte Pumpen-Firmware Sende BZ-Werte zu xDrip+ diff --git a/app/src/main/res/values-el-rGR/objectives.xml b/app/src/main/res/values-el-rGR/objectives.xml index bf0ae05ad6..7941ad4e61 100644 --- a/app/src/main/res/values-el-rGR/objectives.xml +++ b/app/src/main/res/values-el-rGR/objectives.xml @@ -36,7 +36,6 @@ Εμφάνιση περιεχομένου της προσθήκης Κύκλωμα Χρησιμοποιήστε τη λειτουργία κλίμακας πατώντας παρατεταμένα το διάγραμμα BG Εισαγωγή - Εισαγάγετε τον κωδικό που λαμβάνετε από τους προγραμματιστές για να παρακάμψετε τους υπόλοιπους στόχους Κωδικός αποδεκτός Μη έγκυρος κωδικός Αποδείξτε τις γνώσεις σας diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index e15480ce10..cd860161bc 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -1147,8 +1147,6 @@ Αποστολή προσωρινών ρυθμών Αποστολή αλλαγών προφίλ, προσωρινών στόχων Αποστολή BG βαθμονομήσεων - Αλλαγή σε Θερινή ώρα σε 24h ή λιγότερο - Η Θερινή ώρα αλλάζει σε λιγότερο από 3 ώρες - Απενεργοποιήθηκε το κλειστό κύκλωμα περιορισμός εσωτερικής μνήμης Ελευθερώστε τουλάχιστον %1$d MB από εσωτερική μνήμη! Κύκλωμα απενεργοποιήθηκε! Λάθος μορφή αρχείου diff --git a/app/src/main/res/values-es-rES/exam.xml b/app/src/main/res/values-es-rES/exam.xml index d0b8dce294..c3c2af8b25 100644 --- a/app/src/main/res/values-es-rES/exam.xml +++ b/app/src/main/res/values-es-rES/exam.xml @@ -8,9 +8,9 @@ https://androidaps.readthedocs.io/en/latest/EN/Configuration/Config-Builder.html?#insulin El significado es igual al parámetro DIA utilizado en su bomba. Debes determinar tu valor individual (pero no menos de 5 horas). - Tema: Hypo Temp-Objetivo - ¿Cuál es la razón principal para establecer un hipo TT? - Para evitar que BG caiga si ya hay basal temporal a cero corriendo. + Tema: Objetivo Temporal por Hipoglucemia + ¿Cuál es la razón principal para establecer un objetivo temporal por hipoglucemia? + Para evitar que la glucemia caiga si ya hay una basal temporal a cero activada. Para evitar que AAPS inyecte demasiada insulina después de una subida causada por los carbohidratos de acción rápida utilizados para tratar una bajada en las lecturas de glucosa. https://androidaps.readthedocs.io/en/latest/EN/Usage/temptarget.html ¿Qué perfil puede ser usado y configurado estando desconectado? @@ -163,4 +163,7 @@ Tu equipo de diabetes Google Facebook + Otra medicación + La AAPS reduce el BR para aumentar la glucosa en la sangre. Los inhibidores del grupo de las SGLT2 (gliflozinas) pueden prevenir el aumento esperado de BG y, por lo tanto, pueden producir una deficiencia de insulina peligrosa (DKA). +\nLos nombres comunes de marca son: Invokana ®, Forxiga ®, Steglatro ®, Suglat ®, Apleway ®, Synjardy ®, Vokanamet ®, Xigduo ®.\n\nI prometo que no tomaré este tipo de medicamentos cuando use AAPS o desactivará el lazo cerrado antes de usar estas drogas. diff --git a/app/src/main/res/values-es-rES/insight_alert_titles.xml b/app/src/main/res/values-es-rES/insight_alert_titles.xml index f5babbe83a..c47fc15c82 100644 --- a/app/src/main/res/values-es-rES/insight_alert_titles.xml +++ b/app/src/main/res/values-es-rES/insight_alert_titles.xml @@ -10,7 +10,7 @@ Pila baja Fecha/hora no válida Fin de la garantía - TBR cancelada + Basal Temporal cancelada Bolo cancelado Aviso de préstamo de infusora Reservorio no insertado diff --git a/app/src/main/res/values-es-rES/objectives.xml b/app/src/main/res/values-es-rES/objectives.xml index 6252812668..1a24ac5d0c 100644 --- a/app/src/main/res/values-es-rES/objectives.xml +++ b/app/src/main/res/values-es-rES/objectives.xml @@ -36,7 +36,6 @@ Mostrar contenido del plugin Loop Usar función de escala mediante un gráfico BG pulsado largo Intro - Introduzca el código obtenido de los desarrolladores para eludir el resto de objetivos Código aceptado Código no válido Compruebe su conocimiento diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 7ff5910adf..0b5ac4ac22 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -1147,8 +1147,8 @@ Subir basales temporales Subir conmutaciones de perfil, objetivos temps Subir pruebas BG - Cambio al horario de verano/invierno en menos de 24 horas - Cambio al horario de verano/invierno hace menos de 3 horas - Lazo cerrado deshabilitado + Cambio al horario de verano en menos de 24 horas + Cambio al horario de verano hace menos de 3 horas - Lazo cerrado deshabilitado restricción de almacenamiento interno Libera al menos %1$d MB de almacenamiento interno. ¡Loop desactivado! Formato incorrecto @@ -1236,6 +1236,8 @@ No seleccionado (Vista simple) Alcalina (vista extendida) Litio (vista extendida) + NiZn (vista ampliada) + Bolos/Depuración de Tratamientos ESCANEAR DETENER @@ -1373,4 +1375,6 @@ Asistente Bolus min %1$dg + Activado + Desactivado diff --git a/app/src/main/res/values-fr-rFR/objectives.xml b/app/src/main/res/values-fr-rFR/objectives.xml index d9e62aefce..b39e5ce285 100644 --- a/app/src/main/res/values-fr-rFR/objectives.xml +++ b/app/src/main/res/values-fr-rFR/objectives.xml @@ -36,7 +36,7 @@ Affichage du contenu du plugin Boucle Modification de l\'échelle du graphique par un appui long sur la courbe de glycémie Entrer - Entrez le code obtenu auprès des développeurs pour contourner le reste des objectifs + Si vous étiez avant un utilisateur d\'OpenAPS et que votre NS a au moins 3 mois de données de bouclage, vous pouvez envoyer un e-mail à objectives@androidaps.org avec votre adresse NS et demander un code pour contourner le reste des objectifs. Entrez le code obtenu auprès des développeurs Code accepté Code invalide Prouver ses connaissances diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index df7a45cd62..595fd9fdc4 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -155,9 +155,9 @@ Confirmation Entrez le nouveau traitement : Bolus - Bolus : + Bolus: Basal - Basal : + Basal: Glucides Changez vos entrées ! Définir un nouveau bolus étendu @@ -314,7 +314,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S %1$.2fU vont être injectées Vous avez atteint la limite maximale Aucun profil séléctionné - La Boucle été désactivée + La Boucle a été désactivée La Boucle a été activée La Boucle est désactivée La Boucle est activée @@ -322,17 +322,17 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S La valeur %1$s est en dehors des limites La commande à distance n\'est pas autorisée Bolus à distance non disponible. Réessayez plus tard. - Pour démarrer Basal %1$.2fU/h pendant %2$d min, renvoyer le code %3$s + Pour démarrer la Basal %1$.2fU/h pendant %2$d min, renvoyer le code %3$s Pour changer le profil vers %1$s %2$d%%, renvoyer le code %3$s Pour démarrer le Bolus étendu %1$.2fU pendant %2$d min, renvoyer le code %3$s - Pour démarrer le Basal %1$d%% pendant %2$d min, renvoyer le code %3$s + Pour démarrer la Basal %1$d%% pendant %2$d min, renvoyer le code %3$s Envoyer le code %2$s pour suspendre la Boucle pour %1$d minutes Démarrage réussi pour %1$.2fU/h de basal temporaire pour %2$d min Le Bolus étendu %1$.2fU pendant %2$d min a commencé avec succès Démarrage réussi pour %1$d%% de Basal temporaire pour %2$d min Le démarrage du basal temporaire a échoué Le départ du Bolus étendu a échoué - Envoyer le code %1$s pour arrêter le Basal temporaire + Envoyer le code %1$s pour arrêter la Basal temporaire Pour arrêter le Bolus étendu, renvoyer le code %1$s Basal temporaire annulé Bolus étendu annulé @@ -393,7 +393,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S Notification en cours DONNÉES ANCIENNES %1$d min passées - %1$dmin passées + il y a %1$d min Profil Local OpenAPS AMA Delta basé sur une courte moyenne @@ -533,7 +533,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S Delta 15 min GA Superbolus - Démarrage de l\'app journaux vers NS + Démarrage AAPS entré dans NS Sortie de l’application pour appliquer de nouveaux paramètres. DanaRv2 Insuline @@ -544,9 +544,9 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S INS Activer les Superbolus dans l’Assistant Activer la fonctionnalité SuperBolus dans l’Assistant. Ne pas l’activer avant de bien comprendre comment cela fonctionne réellement. IL PEUT PROVOQUER UNE OVERDOSE D’INSULINE SI UTILISÉ AVEUGLÉMENT ! - Afficher les lumières d\'état sur l\'écran d\'accueil - Afficher les lumières d\'état prolongées sur l\'écran d\'accueil - Activer les lumières d\'état prolongées pour AgeC, AgeI, AgeS, niveaux du réservoir et de batterie sur l\'écran d\'accueil. + Afficher les voyants d\'état sur l\'écran d\'accueil + Afficher les voyants d\'état prolongés sur l\'écran d\'accueil + Activer les voyants d\'état prolongés pour AgeC, AgeI, AgeS, niveaux du réservoir et de batterie sur l\'écran d\'accueil. Seuil d\'avertissement de niveau du réservoir [U] Seuil critique de niveau du réservoir [U] Seuil d’avertissement du niveau de batterie [%] @@ -681,6 +681,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S g m h + j ]]> kJ En @@ -705,13 +706,14 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S BT Watchdog Ceci va arrêter le Bluetooth du téléphone pour une seconde si la connexion pompe n’est pas possible. Cela peut assister certains téléphones dont la connexion Bluetooth se bloque. App Eversense (patché) - Remonter les données glycémiques vers NS + Remonter les Gly vers NS Paramètres de téléchargement des glycémies Afficher le delta détaillé Afficher delta avec une décimale supplémentaire + SMB minutes max Max. minutes de basal pour limiter le SMB Firmware pompe incompatible - Transmettre les données G vers xDrip+ + Transmettre les Gly vers xDrip+ Dans xDrip+ veuillez séléctionner 640g/Eversense comme source de données Glycémie NSClient Valeur de basal remplacée par la valeur minimale autorisée : %1$s @@ -869,7 +871,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S utilisation dangereuse La lecture du statut a échoué Enregistrer changement de site de cathéter - Enregistrer changement de réservoir + Enreg. changement de réservoir SMB toujours et post-ingestion de glucides désactivé car la source de glycémies actuelle ne supporte pas de filtrage avancé SMB non autorisé en mode Boucle Ouverte Aliments @@ -1148,8 +1150,8 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S Transférer les Basal temporaires Transférer les changements de profils, les objectifs temporaires Transférer les tests de glycémies - Changement d\'heure d\'été dans moins de 24 heures - Changement d\'heure d\'été dans moins de 3 heures - Boucle fermée désactivée + Changement d\'heure dans moins de 24 heures + Changement d\'heure dans moins de 3 heures - Boucle fermée désactivée stockage interne limité Boucle désactivée ! Libérez au moins %1$d Mo du stockage interne ! Format incorrect diff --git a/app/src/main/res/values-it-rIT/exam.xml b/app/src/main/res/values-it-rIT/exam.xml index 5240fa0c8f..4b9047c775 100644 --- a/app/src/main/res/values-it-rIT/exam.xml +++ b/app/src/main/res/values-it-rIT/exam.xml @@ -28,8 +28,8 @@ Esportarle localmente dal menu Manutenzione. Salvare il file esportato in un altro posto come l\'email, Dropbox, Google drive… Esportarle subito dopo l\'installazione di AAPS. - Esportarli dopo aver fatto modifiche d\'impostazione. - Esportarli dopo il completamento di un obiettivo. + Esportarle dopo aver fatto modifiche d\'impostazione. + Esportarle dopo il completamento di un obiettivo. Esportarle quando termini le configurazioni iniziali. https://androidaps.readthedocs.io/en/latest/EN/Usage/ExportImportSettings.html https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#what-emergency-equipment-is-recommended-to-take-with-me diff --git a/app/src/main/res/values-it-rIT/objectives.xml b/app/src/main/res/values-it-rIT/objectives.xml index c78bbf5034..6097ec7ddd 100644 --- a/app/src/main/res/values-it-rIT/objectives.xml +++ b/app/src/main/res/values-it-rIT/objectives.xml @@ -32,18 +32,18 @@ Simula la doccia. Disconnetti il micro per 1h (premi a lungo su Loop aperto) ... e riconnetti allo stesso modo Crea un target temporaneo personalizzato con una durata di 10 min (premi a lungo sul tuo target corrente) - Nel Configuratore attiva il plugin Azioni, rendilo visibile e visualizzane i contenuti tramite il menu in alto + Nel Configuratore strutturale attiva il plugin Azioni, rendilo visibile e visualizzane i contenuti tramite il menu in alto Visualizza il contenuto del plugin Loop Usa la funzione di ridimensionamento premendo a lungo sul grafico delle glicemie Entra - Inserisci il codice ottenuto dagli sviluppatori per aggirare il resto degli obiettivi + Se prima eri un utente OpenAPS e il tuo NS ha almeno 3 mesi di dati in loop, puoi inviare un\'e-mail a objectives@androidaps.org con il tuo indirizzo NS e richiedere il codice per ignorare il resto degli obiettivi. Inserisci il codice ottenuto dagli sviluppatori Codice accettato Codice non valido Dai prova della tua conoscenza Studia e rispondi correttamente alle domande Risposta disabilitata per: %1$s Risposta sbagliata! - Prossimo non completato + Prossimo N.C. Codice richiesta: %1$s (segna tutte le risposte corrette) https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#what-to-do-when-taking-a-shower-or-bath diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index 49c7dcab6b..08b124cd5f 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -7,7 +7,7 @@ Sicurezza trattamenti Max bolo consentito [U] - Max carboidrati consentiti [g] + Max CHO consentiti [g] Preferenze Ricarica trattamenti da NS Resetta database @@ -18,7 +18,7 @@ Utilizza sempre valori basali assoluti Per favore riavvia il tuo telefono oppure fai ripartire AndroidAPS dalle impostazioni di sistema \naltrimenti Android APS non farà il log (è importante monitorare e verificare che gli algoritmi stiano funzionando correttamente)! Questo dispositivo non sembra supportare la whitelist dell\'ottimizzazione della batteria: potrebbero verificarsi problemi di prestazioni. - Alcuni pulsanti per accedere rapidamente alle funzioni comuni + Alcuni tasti per accedere rapidamente alle funzioni comuni Inserisci voci di registro avanzate. Utilizzato per configurare i plugin attivi Programma di apprendimento @@ -31,7 +31,7 @@ Stato dell\'algoritmo nel 2016 Stato dell\'algoritmo nel 2017 Algoritmo più recente per gli utenti avanzati - Visualizza lo stato corrente del tuo loop e i pulsanti per le azioni più comuni + Visualizza lo stato corrente del tuo loop e i tasti per le azioni più comuni Mostra una notifica con una breve panoramica di ciò che sta facendo il tuo loop Definisci un profilo disponibile offline. Fornisce il profilo definito in Nightscout @@ -43,15 +43,15 @@ Integrazione del microinfusore DANA Diabecare RS Per le persone in terapia multi-iniettiva Per microinfusori che non hanno ancora alcun driver (Loop Aperto) - La sensibilità è calcolata allo stesso modo di Oref0, ma puoi specificare l\'intervallo di tempo al passato. L\'assorbimento minimo dei carboidrati è calcolato da \'max tempo assorbimento carboidrati\' nelle preferenze. + La sensibilità è calcolata allo stesso modo di Oref0, ma puoi specificare l\'intervallo di tempo al passato. L\'assorbimento minimo dei carboidrati è calcolato da \'max tempo assorbimento pasto\' nelle preferenze. La sensibilità è calcolata dai dati delle ultime 24h e i carboidrati (se non assorbiti) vengono tagliati fuori dopo il tempo specificato nelle preferenze. La sensibilità è calcolata dai dati delle ultime 8h e i carboidrati (se non assorbiti) vengono tagliati fuori dopo il tempo specificato nelle preferenze. Il Plugin calcola anche UAM. - La sensibilità è calcolata come media ponderata dalle deviazioni. Le deviazioni più recenti hanno peso maggiore. L\'assorbimento minimo dei carboidrati è calcolato da \'max tempo assorbimento carboidrati\' nelle preferenze. Questo algoritmo è il più veloce nel seguire i cambiamenti di sensibilità. - Ricevi valori Glicemia dall\'app Eversense modificata. - Ricevi valori Glicemia da Glimp. - Ricevi valori Glicemia da 600SeriesAndroidUploader. - Scarica dati Glicemia da Nightscout - Ricevi dati Glicemia da xDrip. + La sensibilità è calcolata come media ponderata dalle deviazioni. Le deviazioni più recenti hanno peso maggiore. L\'assorbimento minimo dei carboidrati è calcolato da \'max tempo assorbimento pasto\' nelle preferenze. Questo algoritmo è il più veloce nel seguire i cambiamenti di sensibilità. + Ricevi valori glicemia dall\'app Eversense modificata. + Ricevi valori glicemia da Glimp. + Ricevi valori glicemia da 600SeriesAndroidUploader. + Scarica dati glicemia da Nightscout + Ricevi dati glicemia da xDrip. Salva tutti i trattamenti che sono stati fatti Monitora e controlla AndroidAPS usando il tuo smartwatch WearOS. Mostra le informazioni del loop sulla watchface di xDrip+. @@ -64,7 +64,7 @@ Target NESSUN PROFILO IMPOSTATO Insulina: - Carboidrati: + CHO: IOB: IOB: IOB totale: @@ -75,15 +75,15 @@ IOB: IOB totale: Insulina - Carbs + CHO BG TT - Carbs - Corr + CHO + Correzione U IOB da bolo Esegui ora - MICROINFUSORE VIRTUALE + MICRO VIRTUALE Velocità basale originale Basale temporanea Bolo esteso @@ -108,7 +108,7 @@ Glicemia Delta Delta: - Configurazione strutturale + Configuratore strutturale Obiettivi OpenAPS MA Panoramica @@ -116,7 +116,7 @@ Profilo semplice Basale temporanea Trattamenti - Microinfusore Virtuale + Micro Virtuale Portale Micro Quale microinfusore desideri utilizzare con AndroidAPS? @@ -158,10 +158,10 @@ Bolo: Basale Basale - Carbs + CHO Cambia il tuo input! Imposta nuovo bolo esteso: - Origine Glicemia + Origine glicemia Da dove AndroidAPS dovrebbe ottenere i suoi dati? xDrip Modalità APS @@ -177,8 +177,8 @@ LOOP DISABILITATO DAI VINCOLI IOB da basale Vincolo bolo applicato - Vincolo carboidrati applicato - Controllo BG + Vincolo CHO applicato + Controllo glicemia Annuncio Nota Domanda @@ -194,16 +194,16 @@ Bolo combo Inizio basale temporanea Fine basale temporanea - Correzione con carboidrati + Correzione con CHO OpenAPS Offline Tipo di evento Altro Glucometro Sensore - Carbs + CHO Insulina - Tempo carboidrati - Fraziona + Tempo CHO + Frazione Durata Percentuale Assoluto @@ -224,11 +224,11 @@ File non trovato Esporta impostazioni Importa impostazioni - Max U/h che una basale temporanea può impostare + Max U/h a cui limitare una basale temporanea Questo valore è chiamato max basale nel contesto OpenAPS - Max IOB da basale che OpenAPS può erogare [U] - Questo valore è chiamato Max IOB nel contesto OpenAPS\nIndica l\'insulina massima in [U] che APS può erogare contemporaneamente. - RESPINGI + Max IOB da basale a cui limitare OpenAPS [U] + Questo valore è chiamato Max IOB nel contesto OpenAPS\nIndica l\'insulina massima in [U] che APS può erogare in contemporanea. + RIMUOVI DanaR Connessione Connesso @@ -260,11 +260,23 @@ Numeri di telefono consentiti +XXXXXXXXXX;+YYYYYYYYYY Per erogare il bolo di %1$.2fU rispondi col codice %2$s + Per erogare il bolo pasto di %1$.2fU rispondi col codice %2$s + Per impostare il Temp-Target %1$s rispondi con il codice %2$s + Per cancellare il Temp-Target rispondi col codice %1$s + Per disabilitare il Servizio di Controllo Remoto tramite SMS rispondi col codice %1$s.\n\nRicorda che potrai riattivarlo solo in maniera diretta dallo smartphone master in cui è installato AAPS. + Servizio di Controllo Remoto tramite SMS stoppato. Per riattivarlo, usa lo smartphone master in cui è installato AAPS. Per inviare la calibrazione %1$.2f rispondi col codice %2$s Bolo fallito + Numero minimo di minuti che devono trascorrere tra un bolo remoto e il successivo + Quanti minuti devono trascorrere, almeno, tra un bolo e il successivo + Per la tua sicurezza, per modificare questa preferenza hai bisogno di aggiungere almeno 2 numeri di telefono. Bolo di %1$.2fU erogato con successo Sto per erogare %1$.2fU Bolo di %1$.2fU erogato con successo + Bolo pasto di %1$.2fU erogato con successo + Target %1$s per %2$d minuti + Target %1$s per %2$d minuti impostato con successo + Temp-Target cancellato con successo Erogazione di %1$.2fU Consenti comandi remoti tramite SMS Dito @@ -324,10 +336,13 @@ Per avviare la basale %1$.2fU/h per %2$d min rispondi col codice %3$s Per passare al profilo %1$s %2$d%% rispondi col codice %3$s Per avviare il bolo esteso %1$.2fU/h per %2$d min rispondi col codice %3$s + Per inserire %1$dg a %2$s rispondi col codice %3$s Per avviare la basale %1$d%% per %2$d min rispondi col codice %3$s Per sospendere il loop per %1$d minuti rispondi col codice %2$s Basale temporanea %1$.2fU/h per %2$d min avviata con successo Bolo esteso %1$.2fU/h per %2$d min avviato con successo + CHO %1$dg inseriti con successo + Inserimento di %1$dg di CHO fallito Basale temporanea %1$d%% per %2$d min avviata con successo Avvio basale temporanea fallito Avvio bolo esteso fallito @@ -338,26 +353,26 @@ Cancellazione basale temporanea fallita Cancellazione bolo esteso fallita Comando sconosciuto o risposta errata - QuickWizard - Impostazioni QuickWizard - Testo pulsante: - Carboidrati: + Calcolatore Rapido + Impostazioni Calcolatore Rapido + Testo: + CHO: Valido: Aggiungi Modifica Rimuovi Pasto - Corr + Correzione Azioni AndroidAPS avviato - NS: solo upload (sincronizzazione disabilitata) + NS: solo upload (sincron. disabilitata) NS: solo upload. Non ha effetto su SGV a meno che non sia selezionata una fonte locale come xDrip. Non ha effetto sui Profili durante l\'utilizzo dei profili NS. Micro non inizializzato! Micro non inizializzato, profilo non impostato! Carica/Riempi Assicurati che la quantità corrisponda alla specifica del tuo set di infusione! Altro - Quantità standard di insulina per la funzione Carica/Riempi. + Quantità standard di insulina per Carica/Riempi. Tasto 1 Tasto 2 Tasto 3 @@ -370,7 +385,7 @@ Limite alto e basso per i grafici nella sezione Panoramica e sullo Smartwatch Limite BASSO Limite ALTO - Wear + Smartwatch Invia di nuovo tutti i dati Apri impostazioni sullo smartwatch Errore micro @@ -385,8 +400,8 @@ Abilita bolo esteso sul micro Cambia la modalità da U/d a U/h nel micro Valore basale inferiore al minimo. Profilo non impostato! - BG: - Ultimo BG: + Glicemia: + Ultima glicemia: MDI MM640g Notifica in corso @@ -404,7 +419,7 @@ Ricarica eventi da NS Elimina trattamenti nel futuro Pasto a breve - Ipo + Ipoglicemia Attività Rimuovi record: Statistiche DanaR @@ -435,10 +450,10 @@ TRATT PT OBT - WEAR + SMWA SMS Abbrevia i titoli delle schede - Utilizza sempre il delta medio ridotto anziché il delta semplice + Utilizza sempre il delta medio ridotto Utile quando i dati provenienti da sorgenti non filtrate come xDrip diventano \"rumorosi\" (instabili). Impostazioni avanzate Modello:%1$02X Protocollo:%2$02X Codice:%3$02X @@ -451,7 +466,7 @@ [Valore predefinito: vero]\nViene utilizzato per consentire ad autosens di regolare i target glicemici, in aggiunta a ISF e basali. [Valore predefinito: 2]\nBolus snooze è attivato dopo un bolo pasto per fare in modo che il loop non imposti basali temporanee basse quando hai appena mangiato. AndroidAPS non imposterà velocità basali troppo basse nel periodo corrispondente a DIA diviso il parametro bolus snooze - divisore DIA. Con DIA di 3 ore \"bolus snooze\" durerà 1.5 ore (3/2). [Valore predefinito: 3.0 (AMA) o 8.0 (SMB)]. Questa è un\'impostazione per l\'impatto di assorbimento predefinito dei carboidrati in 5 minuti. L\'impostazione predefinita è una previsione di 3mg/dl/5min. Ha effetto sulla velocità di decadimento dei COB (carboidrati attivi) e su quanto il loro assorbimento incide nella previsione dell’andamento glicemico, quando la glicemia sta scendendo più del previsto o non sta salendo quanto previsto. - Attenzione!\nNormalmente non dovresti modificare questi valori. FAI CLICK QUI e leggi il testo e assicuratevi di AVERLO CAPITO prima di cambiare uno di questi valori. + Attenzione!\nNormalmente non dovresti modificare questi valori. FAI CLICK QUI e leggi il testo e assicurati di AVERLO CAPITO prima di cambiare uno di questi valori. Sono consentite solo cifre numeriche. Sono consentite solo cifre numeriche nel range %1$s - %2$s. Il campo non deve essere vuoto @@ -489,17 +504,17 @@ API secret di NS API secret di NS Inserisci l\'API secret di NS (minimo 12 caratteri) - Invia adesso + Invia ora Cancella coda Mostra coda Coda: Stato: In pausa - Cancella il log + Cancella log NSCLIENT non ha il permesso di scrittura. API secret errato? Impostazioni smartwatch Mostra IOB dettagliato - Dividi IOB tra boli e IOB da basale sulla watchface + Dividi IOB in bolo e basale sulla watchface non riuscito - controlla il telefono Non disponibile Età del paziente @@ -541,21 +556,21 @@ Novorapid, Novolog, Humalog Fiasp INS - Abilita superbolo nel wizard - Abilita la funzionalità superbolo nel wizard. Non abilitare fino a quando non impari ciò che realmente fa. PUÒ CAUSARE SOVRADOSAGGIO DI INSULINA SE USATO IMPROPRIAMENTE! - Mostra \"indicatori di stato\" sulla schermata iniziale - Mostra \"indicatori di stato\" estesi sulla schermata iniziale + Abilita superbolo nel calcolatore + Abilita la funzionalità superbolo nel calcolatore. Non abilitare fino a quando non impari ciò che realmente fa. PUÒ CAUSARE SOVRADOSAGGIO DI INSULINA SE USATO IMPROPRIAMENTE! + Mostra indicatori di stato sulla home + Mostra indicatori estesi sulla home Abilita indicatori di stato estesi per \"età\" cannula, insulina, sensore, serbatoio e livello batteria sulla schermata iniziale. Soglia di avviso livello serbatoio [U] - Soglia critica livello serbatoio [U] - Soglia livello batteria a cui prestare attenzione [%] + Soglia livello serbatoio critico [U] + Soglia di avviso livello batteria [%] Soglia livello batteria critico [%] IOB COB Firmware Ultima connessione Stato bluetooth - Riguardo a + Informazioni su Autorizzazione SMS mancante Autorizzazione stato telefono mancante Stato xDrip (smartwatch) @@ -606,7 +621,7 @@ SAGE IAGE CAGE - PBAGE + MBAGE OAPS UPLD BAS @@ -622,7 +637,7 @@ Abilita le trasmissioni ad altre app (come xDrip). Non abilitare se hai installato più di un\'istanza di AAPS o NSClient! Abilita le trasmissioni locali. ATTIVITÀ & FEEDBACK - CARBOIDRATI & BOLO + CHO & BOLO CGM & OPENAPS MICRO Valore basale [U/h] @@ -680,6 +695,7 @@ g m h + g ]]> kJ Enr @@ -693,9 +709,9 @@ Driver del micro corretto Micro irraggiungibile Letture glicemia mancanti - Utilizza le notifiche di sistema per avvisi e notifiche + Usa le notifiche di sistema per gli avvisi Allarmi locali - Allarme se non si ricevono dati Glicemia + Allarme se non si ricevono dati glicemia Allarme se il micro non è raggiungibile Soglia micro irraggiungibile [min] Allarme urgente @@ -708,6 +724,7 @@ Impostazioni caricamento glicemia Mostra delta dettagliato Mostra delta con una cifra decimale in più + Max minuti SMB Max minuti di basale a cui limitare SMB Firmware del micro non supportato Invia dati glicemia a xDrip+ @@ -741,7 +758,7 @@ Nessuna connessione per %1$d min %1$d%% (%2$d min restanti) Inizializzazione - Sospeso a causa di errore + Sospeso a causa di un errore Sospeso dall\'utente In esecuzione Cancellazione TBR @@ -775,9 +792,9 @@ fa %1$.2f h Abilita SMB sempre - Abilita SMB sempre, indipendentemente dai boli. Possibile solo con sorgente glicemie con un buon filtraggio dei dati, come G5 - Abilita SMB dopo i carboidrati - Abilita SMB per 6h dopo i carboidrati, anche con 0 COB. Possibile solo con sorgente glicemie con un buon filtraggio dei dati, come G5 + Abilita SMB sempre, indipendentemente dai boli. Possibile solo con sorgente glicemia con un buon filtraggio dei dati, come G5 + Abilita SMB dopo i CHO + Abilita SMB per 6h dopo i carboidrati, anche con 0 COB. Possibile solo con sorgente glicemia con un buon filtraggio dei dati, come G5 Abilita SMB con COB Abilita SMB quando COB è attivo (ci sono carboidrati non assorbiti). Abilita SMB con target temporanei @@ -787,7 +804,7 @@ Lascia eseguire la basale temporanea Muto Insulina - Carbs + CHO Tasti Invia una calibrazione a xDrip+ o apre la finestra di calibrazione del G5 Apre xDrip+, il tasto indietro torna ad AAPS @@ -819,8 +836,8 @@ Attività Sensibilità Deviazioni - Carboidrato attivi (COB) - Insulina attiva (IOB) + CHO attivi + Insulina attiva Basali Nessuna azione selezionata, non succederà nulla Avvia TT Ipoglicemia @@ -853,8 +870,8 @@ Limitazione max velocità basale a %1$.2f U/h a causa di: %2$s limite micro deve essere un valore positivo - moltiplicatore - max basale - moltiplicatore - max basale giornaliera + moltiplicatore max basale + moltiplicatore max basale giornaliera Un bolo è stato erogato negli ultimi 3 minuti, SMB ignorato Basale impostata correttamente Limitazione max tasso percentuale a %1$d%% a causa di: %2$s @@ -869,13 +886,13 @@ Lettura stato fallita Registra cambio posizione cannula Registra cambio serbatoio insulina - Le funzioni \"SMB sempre\" e \"SMB dopo i carboidrati\" sono disabilitate perché la sorgente delle glicemie non supporta il filtraggio avanzato + Le funzioni \"SMB sempre\" e \"SMB dopo i CHO\" sono disabilitate perché l\'attuale sorgente delle glicemie non supporta il filtraggio avanzato SMB non consentito in modalità loop aperto Cibo reset In attesa della sincronizzazione dell\'ora (%1$d sec) Disconnesso (%1$d m) - Max IOB totale oltre la quale OpenAPS non può andare [U] + Max IOB totale a cui limitare OpenAPS [U] Questo valore è chiamato Max IOB nel contesto OpenAPS\nOpenAPS non aggiungerà ulteriore insulina se IOB corrente è maggiore di questo valore Micro stoppato Micro avviato @@ -883,7 +900,7 @@ Max tempo assorbimento pasto [h] Tempo entro il quale ogni pasto si considera assorbito. Eventuali carboidrati rimanenti verranno tagliati fuori. Tempo - Mostra campo note nelle finestre trattamento + Finestre tratt.nto: mostra campo note Avanti Indietro Configurazione guidata @@ -893,9 +910,9 @@ Primo incremento di insulina Secondo incremento di insulina Terzo incremento di insulina - Primo incremento di carboidrati - Secondo incremento di carboidrati - Terzo incremento di carboidrati + Primo incremento di CHO + Secondo incremento di CHO + Terzo incremento di CHO CGM Usa solo connessione WiFi WiFi SSID @@ -913,9 +930,9 @@ Definizione micro Bolo: Step=%1$s\nBolo Esteso: [Step=%2$s, Durata=%3$smin-%4$sh]\nBasale: Step=%5$s\nTBR: %6$s (di %7$s), Durata=%8$smin-%9$sh\n%10$s * Sono supportati solo valori discreti, non intervalli di valori, come incrementi per basale/bolo nel micro virtuale. - Riempimento automatico BG - Impostazioni Wizard - Calcoli inclusi nel risultato di Wizard: + Riempimento automatico glicemie + Impostazioni Calcolatore + Calcoli inclusi nel risultato del Calcolatore: Impostazioni di visualizzazione Impostazioni generali Abilita NSClient @@ -924,14 +941,14 @@ Lettura stato Le modifiche devono essere fatte in NS Salta configurazione guidata - Premi il pulsante in basso per permettere ad AndroidAPS di suggerire/fare modifiche alla basale + Premi il tasto in basso per permettere ad AndroidAPS di suggerire/fare modifiche alla basale Configura il plugin APS Configura il plugin di Sensibilità Il plugin di Sensibilità è utilizzato per il rilevamento della sensibilità all\'insulina e il calcolo di COB. Per ulteriori informazioni visita: https://github.com/MilosKozak/AndroidAPS/wiki/Sensitivity-detection-and-COB NSClient gestisce la connessione a Nightscout. Puoi saltare questa parte ora, ma non sarai in grado di superare gli obiettivi fino a quando non ne porterai a termine la configurazione. Ricorda: i nuovi profili di insulina richiedono una DIA di almeno 5h. DIA di 5 - 6h sui nuovi profili sono uguali a DIA di 3h sui vecchi profili di insulina. - Configura sorgente glicemie + Configura sorgente glicemia Seleziona il tipo di profilo. Se il paziente è un bambino dovresti utilizzare il profilo di NS. Se non c\'è nessuno a seguirti su Nightscout probabilmente preferirai il profilo locale. Ricorda che stai solo selezionando la sorgente del profilo. Per utilizzarlo devi attivarlo tramite l\'esecuzione del comando \"Cambio profilo\" Seleziona uno degli algoritmi disponibili. Sono ordinati dal più vecchio al più recente. L\'algoritmo più recente è solitamente più potente e più aggressivo. Pertanto, se sei un nuovo utente, probabilmente dovresti iniziare con AMA e non con l\'ultimo. Non dimenticare di leggere la documentazione di OpenAPS e di configurarlo prima dell\'uso. Avvia il tuo primo obiettivo @@ -981,12 +998,12 @@ Dati trattamento incompleti Impostazioni manutenzione Email - Nessun log da inviare + Numero di log da inviare Manutenzione MANUT Fornisce numerose funzioni per la manutenzione (ad es. invio log, eliminazione log). - Invia i log via Email - Elimina i log + Invia log via Email + Elimina log Un trattamento (insulina: %1$.2f, carboidrati: %2$d, a: %3$s) non può essere aggiunto ai trattamenti. Controlla e aggiungi il record necessario. eCarbs: %1$d g (%2$d h), ritardo: %3$d m Nessun dato autosens disponibile @@ -995,7 +1012,7 @@ Malfunzionamento NSClient. Considera il riavvio di NS e NSClient. AS Versione %1$s disponibile - Offset temporale + Offset Modalità APS preferita Totale Calc @@ -1061,7 +1078,7 @@ Abilita notifica di fine TBR\n(impostazione micro) Disabilita notifica di fine TBR\n(impostazione micro) Ricarica - Integrazione micro Accu-Chek Insight + Integrazione microinfusore Accu-Chek Insight Non inserito Ultima conn: %1$d min fa TBR: %1$d%% per %2$d / %3$d min @@ -1358,10 +1375,10 @@ Vincolo Bolo applicato: %2$.2fU a %3$.2fU]]> !!!!! Rilevato assorbimento lento dei carboidrati: %2$d%% del tempo. Ricontrolla il tuo calcolo. COB potrebbero essere sovrastimati e potrebbe essere somministrata più insulina !!!!!]]> %1$.0f / %2$d U - Eroga questa parte del risultato del bolus wizard [%] - Bolus wizard esegue il calcolo, ma solo questa parte dell\'insulina calcolata è erogata. Utile con algoritmo SMB. + Eroga parte del risultato del calcolatore [%] + Il calcolatore esegue il calcolo, ma solo questa parte dell\'insulina calcolata è erogata. Utile con algoritmo SMB. Caricamento ... - Snooze + Posticipa Intervallo di tempo Il tempo è compreso tra %1$s e %2$s Tra @@ -1372,7 +1389,7 @@ %1$.0fg IC: %2$.1f %1$.1fg IC: %2$.1f %1$d%% - Bolus wizard + Calcolatore min %1$dg On diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 6fc3797b17..b88368882e 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -1140,8 +1140,6 @@ 임시 Basal 업로드 프로파일변경, 임시목표 업로드 혈당 테스트 업로드 - 24시간 이내 썸머타임 변경 - 썸머타임 변경이 3시간 미만입니다 - Closed Loop 비활성됨 내부 저장 용량 제한 내부 저장 공간을 최소 %1$d MB 이상 비우세요! Loop가 비활성화되었습니다! 잘못된 형식 diff --git a/app/src/main/res/values-lt-rLT/objectives.xml b/app/src/main/res/values-lt-rLT/objectives.xml index 3a3bf2bcac..95034771a6 100644 --- a/app/src/main/res/values-lt-rLT/objectives.xml +++ b/app/src/main/res/values-lt-rLT/objectives.xml @@ -36,7 +36,6 @@ Parodyti Ciklo įskiepio turinį Panaudokite vaizdo dydžio keitimo funkciją ilgai spaudžiant ant glikemijos kreivės Įeiti - Įveskite kūrėjo kodą, kad apeitumėte likusius tikslus Kodas priimtas Neteisingas kodas Patvirtinkite savo žinias diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index b857748b1a..0d52246cec 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -202,7 +202,7 @@ Sensorius Angliavandeniai Insulinas - Angliavandenių laikas + AV laikas Išskaidyti Trukmė Procentai @@ -988,7 +988,7 @@ Siųsti įrašus el. paštu Ištrinti įrašus Įrašas (insulinas: %1$.2f, angl.: %2$d, ties: %3$s) negali būti pridėtas. Prašome patikrinti ir rankiniu būdu atitinkamai įtraukti įrašą. - eAngl.: %1$d g (%2$d h), atidėjimas: %3$d m + iAV.: %1$d g (%2$d h), atidėjimas: %3$d m Nėra autosens duomenų Įrašų nustatymai Atkurti numatytuosius @@ -1147,8 +1147,6 @@ Perkelti laikiną bazę Perduoti profilio pokyčius, laikinas bazes Perduoti KG testus - Laiko persukimas įvyks po 24 val ar mažiau - Laikas persuktas prieš mažiau nei 3 val - Uždaras ciklas deaktyvuotas vidinės saugyklos apribojimas Atlaisvinkite ne mažiau kaip %1$d MB iš vidinės atminties! Ciklas išjungtas! Neteisingas formatas diff --git a/app/src/main/res/values-nl-rNL/exam.xml b/app/src/main/res/values-nl-rNL/exam.xml index 377eba5335..489aec894a 100644 --- a/app/src/main/res/values-nl-rNL/exam.xml +++ b/app/src/main/res/values-nl-rNL/exam.xml @@ -143,7 +143,7 @@ Basalen zullen 10% hoger zijn. Basalen zullen 10% lager zijn. De KH ratio wordt 10% hoger. - De KH ratio wordt 10% hoger. + De KH ratio wordt 10% lager. ISF-waarde wordt 10% hoger. ISF-waarde wordt 10% lager. In totaal zul je ongeveer 10% minder insuline krijgen. diff --git a/app/src/main/res/values-nl-rNL/objectives.xml b/app/src/main/res/values-nl-rNL/objectives.xml index f605592027..f85af9f2c5 100644 --- a/app/src/main/res/values-nl-rNL/objectives.xml +++ b/app/src/main/res/values-nl-rNL/objectives.xml @@ -36,7 +36,7 @@ Inhoud van loop plugin weergeven Gebruik de schaalfunctie: houd de BG grafiek lang ingedrukt Enter - Voer de code in die u van de ontwikkelaars hebt gekregen om de rest van de doelen te omzeilen + Als u eerder OpenAPS gebruikte en u NS ten minste 3 maanden aan Loop gegevens heeft kunt u een code aanvragen waarmee u de rest van de doelstellingen kunt omzeilen. U kunt de aanvraag indienen via objectives@androidaps.org, samen met de url van uw NS. Voer de code die u van de ontwikkelaars hebt ontvangen in Code geaccepteerd Code ongeldig Bewijs je kennis diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index 5822a1ccaa..02c6464f33 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -1147,8 +1147,6 @@ Tijdelijke basalen uploaden Upload profiel wisselingen, tijdelijke doelen Upload BG-tests - Zomer/wintertijd omschakeling binnen 24 uur - Omschakeling zomer/wintertijd minder dan 3 uur geleden - Closed Loop modus gedeactiveerd interne opslag bijna vol Maak minstens %1$d MB vrij in interne opslag! Loop is uitgeschakeld! Verkeerde invoer @@ -1236,6 +1234,8 @@ Niet geselecteerd (Eenvoudige weergave) Alkaline (uitgebreide weergave) Lithium (uitgebreide weergave) + NiZn (uitgebreide weergave) + Bolus/Behandelingen Debuggen SCAN STOP @@ -1373,4 +1373,6 @@ Bolus wizard min %1$dg + Aan + Uit diff --git a/app/src/main/res/values-pl-rPL/objectives.xml b/app/src/main/res/values-pl-rPL/objectives.xml index 17777caf68..af8c38edd9 100644 --- a/app/src/main/res/values-pl-rPL/objectives.xml +++ b/app/src/main/res/values-pl-rPL/objectives.xml @@ -36,7 +36,6 @@ Wyświetl zawartość wtyczki Pętla (Loop) Użyj funkcji skalowania przez dłuższe przytrzymanie wykresu glikemii Wprowadź - Wprowadź kod otrzymany od programistów by pominąć resztę zadań Kod został zaakceptowany Niepoprawny kod Potwierdź swoje umiejętności diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 12c2770669..34385a52dd 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -1148,7 +1148,7 @@ Prześlij bazy tymczasowe Prześlij zmiany profilu, cele tymczasowe Prześlij testowe BG - Zmiana na czas letni w ciągu 24 godzin lub krócej + Zmiana czasu w 24h lub mniej Zmiana czasu nastąpiła mniej niż 3 godziny temu - Zamknięta pętla wyłączona limit wielkości pamięci wewnętrznej Zwolnij co najmniej %1$d MB z pamięci wewnętrznej! Pętla zatrzymana! @@ -1237,6 +1237,8 @@ Nie wybrany (widok prosty) Alkaliczne (widok rozszerzony) Litowe (widok rozszerzony) + NiZn (widok rozszerzony) + Bolus/Leczenie - debugowanie SKANUJ ZATRZYMAJ @@ -1374,4 +1376,6 @@ Kalkulator bolusa min %1$dg + Włącz + Wyłącz diff --git a/app/src/main/res/values-pt-rBR/objectives.xml b/app/src/main/res/values-pt-rBR/objectives.xml index c2c6db7b39..684b9bfa21 100644 --- a/app/src/main/res/values-pt-rBR/objectives.xml +++ b/app/src/main/res/values-pt-rBR/objectives.xml @@ -36,7 +36,7 @@ Mostrar conteúdo do plugin Loop Utilizar a função de escala premindo longamente gráfico Glic Inserir - Insira o código obtido dos programadores para ignorar o resto dos objectivos + Se era utilizador do OpenAPS antes e o seu NS tem pelo menos 3 meses de dados do loop, pode enviar um e-mail para oobjectives@androidaps.org com seu endereço de NS e pedir código para ignorar o resto dos objectivos. Digite o código obtido dos programadores Código aceite Código inválido Prove seu conhecimento diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 68993b7a55..c86394d14f 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -680,6 +680,7 @@ g m h + d ]]> kJ En @@ -708,6 +709,7 @@ Configurações de upload de BG Mostrar Delta detalho Mostrar delta com mais um ponto decimal + SMB máx. minutos Limite de minutos de basal para SMB Firmware bomba não suportado Enviar dados Glic. para xDrip+ @@ -1236,6 +1238,8 @@ Não seleccionado (Visão simples) Alcalina (Visão estendida) Lithium (visão estendida) + NiZn (Vista Estendida) + Depuração de Bólus/Tratamentos PROCURAR PARAR @@ -1373,4 +1377,6 @@ Assistente de Bólus min %1$dg + Ligado + Desligado diff --git a/app/src/main/res/values-pt-rPT/objectives.xml b/app/src/main/res/values-pt-rPT/objectives.xml index 727c115853..dae6fcc431 100644 --- a/app/src/main/res/values-pt-rPT/objectives.xml +++ b/app/src/main/res/values-pt-rPT/objectives.xml @@ -36,7 +36,7 @@ Mostrar conteúdo do plugin Loop Utilizar a função de escala premindo longamente gráfico Glic Inserir - Insira o código obtido dos programadores para ignorar o resto dos objectivos + Se era utilizador do OpenAPS antes e o seu NS tem pelo menos 3 meses de dados do loop, pode enviar um e-mail para oobjectives@androidaps.org com seu endereço de NS e pedir código para ignorar o resto dos objectivos. Digite o código obtido dos programadores Código aceite Código inválido Prove seu conhecimento diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index e186080045..892b93a1f6 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -680,6 +680,7 @@ g m h + d ]]> kJ En @@ -708,6 +709,7 @@ Configurações de upload de BG Mostrar Delta detalho Mostrar delta com mais um ponto decimal + SMB máx. minutos Limite de minutos de basal para SMB Firmware bomba não suportado Enviar dados Glic. para xDrip+ diff --git a/app/src/main/res/values-ro-rRO/objectives.xml b/app/src/main/res/values-ro-rRO/objectives.xml index 76613910c2..0c4e5ad161 100644 --- a/app/src/main/res/values-ro-rRO/objectives.xml +++ b/app/src/main/res/values-ro-rRO/objectives.xml @@ -36,7 +36,6 @@ Afișați conținutul facilității Buclă Folosiți funcția scală prin apăsarea prelungă a graficului glicemiei Introduceţi - Introduceți codul obținut de la dezvoltatori pentru a sări peste restul obiectivelor Cod acceptat Cod invalid Dovediți-vă cunoștințele diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 38d793ffcb..caff1bb1a7 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -1144,8 +1144,6 @@ Înregistrează bazalele temporare Înregistrează schimbările de profil, țintele temporare Înregistrează testările de glicemie - Schimbare oră vară/iarnă în mai puțin de 24h - Schimbare oră vară/iarnă în mai puțin de 3 ore - buclă dezactivată restricție de stocare internă Eliberați cel puțin %1$d MB din spațiunl de stocare al telefonlui! Buclă dezactivată! Format greșit diff --git a/app/src/main/res/values-ru-rRU/objectives.xml b/app/src/main/res/values-ru-rRU/objectives.xml index ca1f3159d9..97fac47306 100644 --- a/app/src/main/res/values-ru-rRU/objectives.xml +++ b/app/src/main/res/values-ru-rRU/objectives.xml @@ -36,7 +36,7 @@ Просмотр содержимого модуля Цикл Loop Применить функцию масштабирования по долгому нажатию на диаграмму ГК Ввод - Введите код, полученный от разработчиков, чтобы обойти остальные цели + Если прежде вы были пользователем OpenAPS и имеете в NS не менее 3 месяцев данных, отправьте электронное письмо на objectives@androidaps.org с вашим адресом NS и запросом кода для обхода остальных целей. Введите код, полученный от разработчиков Код принят Неверный код Подтвердите ваши знания diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index de60ad8813..67834cd608 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -202,7 +202,7 @@ сенсор углеводы инсулин - Время действия углеводов + Подождать до еды разбивка Длительность действия процент @@ -260,11 +260,23 @@ разрешенные телефонные номера + XXXXXXXXXX; + YYYYYYYYYY Чтобы подать болюс %1$.2fU ответьте кодом %2$s + Чтобы подать болюс %1$.2fU ответьте кодом %2$s + Чтобы установить временную цель %1$s ответьте кодом %2$s + Чтобы отменить временную цель ответьте кодом %1$s + Чтобы отключить службу удаленных SMS-сообщений ответьте кодом %1$s.\n\n Имейте в виду, что вы сможете вновь активировать ее только с основного телефона AAPS. + Удаленная служба SMS остановлена. Для ее реактивации используйте AAPS на главном смартфоне. чтобы отправить калибровку %1$.2f ответьте кодом %2$s Подача болюса не состоялась + Минимальное количество минут между одним удаленным болюсом и следующим + Минимум минут, должных пройти между одним болюсом и следующим + В целях безопасности, для изменения этого параметра необходимо добавить не менее 2 телефонных номеров. Болюс %1$.2fед. подан успешно Начинается подача болюса %1$.2fед. Болюс %1$.2fед. подан успешно + Болюс на еду %1$.2f ед. подан успешно + Цель %1$s на %2$d минут + Цель %1$s на %2$d минут установлена успешно + Временная цель успешно отменена Подается болюс %1$.2fед. разрешить команды через смс палец @@ -298,7 +310,7 @@ гликемия Перезаправка Останов - Связь установлена за %1$d сек + Попытка установить связь: %1$d сек пароль помпы неверный пароль помпы помпа занята @@ -324,10 +336,13 @@ Чтобы подать базал %1$.2fед./ч в течение %2$d мин. ответьте кодом %3$s Для переключения профиля на %1$s %2$d%% ответьте кодом %3$s Для начала подачи пролонгированного болюса %1$.2fед. за %2$d мин. ответьте кодом %3$s + Чтобы ввести %1$d г в %2$s ответьте кодом %3$s Для начала подачи базала %1$d%% на %2$d мин. ответьте кодом %3$s для приостановки цикла на %1$d мин ответьте кодом %2$s врем базал %1$.2fU/h на %2$d мин начат успешно Пролонгированный болюс %1$.2fед. на %2$d мин. начат успешно + Углеводы %1$d г введены успешно + Не удалось ввести %1$d г углеводов Врем. базал %1$d%% на %2$d мин. начат успешно неуспех старта врем базала Не удалось начать подачу пролонгированного болюса @@ -680,6 +695,7 @@ грамм минут ч + дн ]]> кДж Энергия @@ -708,6 +724,7 @@ Параметры загрузки СК Показать подробно дельту Показать дельту еще с одним десятичным знаком + Максимум минут микроболюса SMB Верхний лимит минут базала на SMB Неподдерживаемая версия прошивки помпы Отправить данные СК на xDrip+ @@ -1150,7 +1167,7 @@ Context | Edit Context Загрузить переключения профиля, временные цели Загрузить тесты ГК Переход на летнее/зимнее время через 24 часа или менее - Переход на летнее время меньше 3 часов назад - Замкнутый цикл отключен + Изменение сезонного времени произошло меньше 3 часов назад-Закрытый цикл выключен ограничение по объему карты памяти Освободите по крайней мере %1$d MB из внутренней памяти! Цикл остановлен! Неверный формат @@ -1238,6 +1255,8 @@ Context | Edit Context Не выбрано (Простой вид) Щелочная (Подробный вид) Литиевая (Подробный вид) + NiZn (Подробный вид) + Отладка Болюс/Назначения СКАН СТОП @@ -1375,4 +1394,6 @@ Context | Edit Context Мастер Болюса мин %1$d гр + Вкл. + Выкл. diff --git a/app/src/main/res/values-sk-rSK/objectives.xml b/app/src/main/res/values-sk-rSK/objectives.xml index 8de93ea158..0b55a38e96 100644 --- a/app/src/main/res/values-sk-rSK/objectives.xml +++ b/app/src/main/res/values-sk-rSK/objectives.xml @@ -36,7 +36,7 @@ Zobrazte obsah modulu uzavretý okruh Prepnite režim zobrazenia dlhým stlačením grafu glykémie Zadať - Zadajte kód, ktorý ste získali od vývojárov, aby ste obišli zostávajúce ciele + Ak ste boli používateľom OpenAPS už predtým a Váš NS má dáta uzavretého okruhu za minimálne 3 mesiace, môžete nám zaslať E-mail na adresu objectives@androidaps.org z Vašou adresou NS a požiadať o kód na obídenie zvyšných cieľov. Zadajte kód získaný od vývojárov Kód akceptovaný Neplatný kód Preukážte svoje znalosti diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 9181d43ee7..bb559c8868 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -260,11 +260,23 @@ Povolené telefónne čísla +421XXXXXXXXX;+421YYYYYYYYY Pre podanie bolusu %1$.2fJI odpovedz SMS kódom %2$s + Pre podanie bolusu k jedlu %1$.2fJI odpovedz SMS kódom %2$s + Pre nastavenie dočasného cieľa %1$s odpovedz SMS kódom %2$s + Pre zrušenie dočasného bazálu odpovedzte SMS s kódom %1$s + Pre vypnutie služby SMS komunikátora odpovedz SMS kódom %1$s.\n\n\Majte na pamäti, že opätovná reaktivácia je možná len priamo na AAPS master telefóne. + SMS komunikátor zastavený. Na jeho reaktiváciu, použite AAPS na master telefóne. Pre odoslanie kalibrácie %1$.2f odpovedz SMS kódom %2$s Chyba pri aplikovaní bolusu + Minimálny počet minút, ktorý musí uplynúť medzi jedným vzdialeným bolusom a tým nasledujúcim + Aspoň koľko minút musí uplynúť, medzi jedným vzdialeným bolusom a tým nasledujúcim + Pre vašu bezpečnosť, musíte pridať aspoň 2 telefónne čísla, aby ste zmenili toto prednastavenie. Bolus %1$.2fJI podaný úspešne Podávanie %1$.2fJ inzulínu Bolus %1$.2fJI podaný úspešne + Bolus %1$.2fJI podaný úspešne + Cieľ %1$s na %2$d minút + Cieľ %1$s na %2$d minút bol úspešne nastavený + Dočasný cieľ úspešne zrušený Podávanie %1$.2fJI Povoliť príkazy na diaľku cez SMS Prst @@ -324,10 +336,13 @@ Pre spustenie bazálu %1$.2fJI/h na %2$d min odpovedzte SMS s kódom %3$s Pre prepnutie profilu na %1$s %2$d%% odpovedzte SMS s kódom %3$s Pre spustenie predĺženého bolusu %1$.2fJI na %2$d min odpovedzte SMS s kódom %3$s + Pre zadanie %1$dg na %2$s odpovedz SMS kódom %3$s Pre spustenie bazálu %1$d%% na %2$d min odpovedzte SMS s kódom %3$s Pre pozastavenie uzavretého okruhu na %1$d minút odpovedaj SMS s kódom %2$s Dočasný bazál %1$.2fJI/h spustený na %2$d minút Predĺžený bolus %1$.2fJI na %2$d min úspešne spustený + Sacharidy %1$dg zadané úspešne + Zadanie %1$dg sacharidov sa nepodarilo Dočasný bazál %1$d%% na %2$d minút úspešne spustený Spustenie dočasného bazálu zlyhalo Spustenie predĺženého bolusu zlyhalo @@ -680,6 +695,7 @@ g m h + d ]]> kJ En @@ -708,6 +724,7 @@ Nastavenie nahrávania glykémie Zobrazovať detailné zmeny Zobrazovať rozdiel s jedným desatinným miestom naviac + SMB max. minút Maximálny počet minút bazálu, ku ktorým se limituje SMB Nepodporovaný firmware v pumpe Odosielať glykémie do xDrip+ @@ -1237,6 +1254,7 @@ Alkalické (rozšírené zobrazenie) Líthiové (rozšírené zobrazenie) NiZn (Rozšírené) + Bolus/Ošetrenia ladenie VYHĽADAŤ ZASTAVIŤ @@ -1374,4 +1392,6 @@ Bolusová kalkulačka min %1$dg + ZAP + VYP diff --git a/app/src/main/res/values-sv-rSE/exam.xml b/app/src/main/res/values-sv-rSE/exam.xml index 6c67f14c63..acf2635f62 100644 --- a/app/src/main/res/values-sv-rSE/exam.xml +++ b/app/src/main/res/values-sv-rSE/exam.xml @@ -164,4 +164,7 @@ Google Facebook Annan medicinering + AAPS sänker basalen för att höja glukosmängden i blodet. Mediciner av typen SGLT2-hämmare (glifloziner) kan motverka eller förhindra den väntade glukoshöjningen vilket kan leda till akut insulinbrist och ketoacidos (DKA). +\nVanliga märken som finns på marknaden: Invokana®, Forxiga®, Jardiance®, Steglatro®, Suglat®, Apleway®, Deberza®, Synjardy®, Vokanamet®, Xigduo®.\n\n +Jag lovar härmed att inte ta sådana läkemedel medan jag använder AAPS eller kommer att pausa loopen innan jag tar dessa läkemedel. diff --git a/app/src/main/res/values-sv-rSE/objectives.xml b/app/src/main/res/values-sv-rSE/objectives.xml index a3b1d3adfd..d89538ab6e 100644 --- a/app/src/main/res/values-sv-rSE/objectives.xml +++ b/app/src/main/res/values-sv-rSE/objectives.xml @@ -36,7 +36,6 @@ Visa innehållet i insticksprogrammet \"Loop\" Testa skala om BG-grafen genom att trycka och hålla in fingret på den Enter - Ange kod som erhållits från utvecklare för att kringgå resten av målen Koden godkänd Koden är felaktig Bevisa dina kunskaper diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 66d5e0b734..df56089ac2 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -6,7 +6,7 @@ Säkerhetsbegränsningar för behandlingar - Max tillåten bolus [E] + Max tillåten bolus [U] Max tillåtna KH [g] Inställningar Uppdatera behandlingar från Nightscout @@ -23,8 +23,8 @@ Används för att konfigurera de aktiva insticksprogrammen Inlärningsprogram Visar förutbestämda inställningar för mat i Nightscout - Insulininställning för Humalog, Apidra och Novorapid/Novolog - Insulininställning för Fiasp + Insulininställning för vanliga direktverkande insuliner som Humalog, Lispro, Apidra och Novorapid/Novolog + Insulininställning för ultrasnabba insuliner, t ex Fiasp Låter dig ställa in tidpunkten för toppen på insulinets aktivitet. Bör bara användas av avancerade användare Aktivera eller avaktivera implementationen som kör loopen. Synkroniserar dina data med Nightscout @@ -54,7 +54,7 @@ Eversense-appen. Ladda ner BG-data från Nightscout Ta emot BG-data från xDrip Spara alla behandlingar som gjorts - Följ och kontrollera din AndroidAPS med din WearOS-klocka + Följ och kontrollera AndroidAPS med din Wear OS-klocka Visa AAPS-information på din xDrip-urtavla Fjärrstyr AndroidAPS med SMS-kommandon. Enheter @@ -80,8 +80,8 @@ Eversense-appen. BG TT KH - Korr - E + Korrektion + U Bolus IOB Utför nu Virtuell pump @@ -192,7 +192,7 @@ Eversense-appen. Mellanmålsbolus Måltidsbolus Korrektionsbolus - Kombinationsbolus + Kombi-bolus Temp basal start Temp basal slut KH-korrektion @@ -544,8 +544,8 @@ Eversense-appen. INS Aktivera superbolus i kalkylatorn Aktiverar superbolusfunktionen i kalkylatorn. Aktivera inte innan du förstått hur den fungerar. DEN KAN ORSAKA ÖVERDOSERING AV INSULIN OM INSIKT SAKNAS OM FUNKTIONEN! - Visa statusindikationer på hemskärmen - Visa statusindikatorer på hemskärmen + Visa \"statuslampor\" på hemskärmen + Visa utökade statusindikatorer Aktivera statusindikatorerna för cage, iage, sage, reservoar och batterinivå på hemskärmen. Varningsnivå för reservoar [U] Akut varningsnivå för reservoar [U] @@ -709,6 +709,7 @@ Eversense-appen. Uppladdningsinställningar för BG Visa detaljerad delta Visa delta med en extra decimal + SMB max minuter Max antal minuter som kan bli SMB Pumpens firmware stöds inte Skicka BG-data till xDrip+ @@ -1057,7 +1058,7 @@ Eversense-appen. Stoppade %1$d%% i %2$d / %3$d min Förlängd bolus - Kombinationsbolus + Kombi-bolus %1$.2f / %2$.2f U i %3$d min Aktivera meddelanden om avslutade tempbasaler\n(pumpinställning) Inaktivera meddelanden om avslutade tempbasaler\n(pumpinställning) @@ -1193,8 +1194,8 @@ Eversense-appen. Autosens %1$s %2$s %% Autosens % %3$s %1$s %2$s - Skillnad - BG-skillnad [%1$s] + BG-förändring + BG-förändring [%1$s] Nuvarande plats Plats Lat: @@ -1237,6 +1238,8 @@ Eversense-appen. Ej vald Alkaliskt Litium + NiZn (utökad vy) + Avlusning av Bolus/Behandlingar SÖK AVBRYT @@ -1374,4 +1377,6 @@ Eversense-appen. Kalkylator min %1$dg + + Av diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f256f77aa4..5335791cbe 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1091,8 +1091,6 @@ 上传临时基础率 上传配置文件切换,临时目标 上传血糖值 - 在24小时内或更少时间内更夏令时时间 - 不到3小时前夏令时时间变化了-已禁用闭环 内部储存空间不足 至少 剩余%1$d MB 内部存储!闭环已禁用! 格式错误 From f82c610d16f608f0f5ceac0758bc10cc59d91956 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 29 Nov 2019 12:19:32 +0100 Subject: [PATCH 52/56] allow objective skip only when NS is connected and in RW mode --- .../plugins/constraints/objectives/ObjectivesFragment.kt | 4 +++- .../plugins/constraints/objectives/ObjectivesPlugin.kt | 2 +- .../constraints/objectives/objectives/Objective.java | 2 ++ .../constraints/objectives/objectives/Objective3.java | 6 ++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 5edb199c9a..be6fca9b1b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -210,6 +210,7 @@ class ObjectivesFragment : Fragment() { scrollToCurrentObjective() startUpdateTimer() RxBus.send(EventObjectivesUpdateGui()) + holder.verify.visibility = View.INVISIBLE } else SntpClient.ntpTime(object : SntpClient.Callback() { override fun run() { @@ -242,6 +243,7 @@ class ObjectivesFragment : Fragment() { scrollToCurrentObjective() startUpdateTimer() RxBus.send(EventObjectivesUpdateGui()) + holder.start.visibility = View.VISIBLE } else SntpClient.ntpTime(object : SntpClient.Callback() { override fun run() { @@ -272,7 +274,7 @@ class ObjectivesFragment : Fragment() { scrollToCurrentObjective() RxBus.send(EventObjectivesUpdateGui()) } - if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted) { + if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted && objective.specialActionEnabled()) { // generate random request code if none exists val request = SP.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt())) SP.putString(R.string.key_objectives_request_code, request) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index 5882f84d61..f8b0ca26ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -104,7 +104,7 @@ object ObjectivesPlugin : PluginBase(PluginDescription() fun completeObjectives(activity: Activity, request: String) { val requestCode = SP.getString(R.string.key_objectives_request_code, "") var url = SP.getString(R.string.key_nsclientinternal_url, "").toLowerCase() - if (!url.endsWith("\"")) url = "$url/" + if (!url.endsWith("/")) url = "$url/" @Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { SP.putLong("Objectives_" + "openloop" + "_started", DateUtil.now()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java index c882a921f5..29d1ad85dc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java @@ -107,6 +107,8 @@ public abstract class Objective { return tasks; } + public boolean specialActionEnabled() { return true; } + public void specialAction(Activity activity, String input) {} public abstract class Task { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java index c9ac7d8935..f8eb45bb2a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java @@ -7,6 +7,7 @@ import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; @@ -38,6 +39,11 @@ public class Objective3 extends Objective { }); } + @Override + public boolean specialActionEnabled() { + return NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth; + } + @Override public void specialAction(Activity activity, String input) { ObjectivesPlugin.INSTANCE.completeObjectives(activity, input); From eba66da4a46491cf69367fb43d5d2d2350a5114e Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 29 Nov 2019 12:38:07 +0100 Subject: [PATCH 53/56] allow go back with objectives --- .../objectives/ObjectivesFragment.kt | 25 +++++++++++-------- .../objectives/objectives/Objective.java | 4 --- .../objectives/objectives/Objective5.java | 5 ---- app/src/main/res/layout/objectives_item.xml | 11 ++++++-- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index be6fca9b1b..cda0b3ed8b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -126,7 +126,6 @@ class ObjectivesFragment : Fragment() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val objective = ObjectivesPlugin.objectives[position] holder.title.text = MainApp.gs(R.string.nth_objective, position + 1) - holder.revert.visibility = View.GONE if (objective.objective != 0) { holder.objective.visibility = View.VISIBLE holder.objective.text = MainApp.gs(objective.objective) @@ -142,6 +141,8 @@ class ObjectivesFragment : Fragment() { holder.verify.visibility = View.GONE holder.progress.visibility = View.GONE holder.accomplished.visibility = View.GONE + holder.unFinish.visibility = View.GONE + holder.unStart.visibility = View.GONE if (position == 0 || ObjectivesPlugin.objectives[position - 1].isAccomplished) holder.start.visibility = View.VISIBLE else @@ -152,15 +153,16 @@ class ObjectivesFragment : Fragment() { holder.progress.visibility = View.GONE holder.start.visibility = View.GONE holder.accomplished.visibility = View.VISIBLE + holder.unFinish.visibility = View.VISIBLE + holder.unStart.visibility = View.GONE } else if (objective.isStarted) { holder.gate.setTextColor(-0x1) holder.verify.visibility = View.VISIBLE holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked holder.start.visibility = View.GONE holder.accomplished.visibility = View.GONE - if (objective.isRevertable) { - holder.revert.visibility = View.VISIBLE - } + holder.unFinish.visibility = View.GONE + holder.unStart.visibility = View.VISIBLE holder.progress.visibility = View.VISIBLE holder.progress.removeAllViews() for (task in objective.tasks) { @@ -264,13 +266,13 @@ class ObjectivesFragment : Fragment() { } }, NetworkChangeReceiver.isConnected()) } - holder.revert.setOnClickListener { - objective.accomplishedOn = 0 + holder.unStart.setOnClickListener { objective.startedOn = 0 - if (position > 0) { - val prevObj = ObjectivesPlugin.objectives[position - 1] - prevObj.accomplishedOn = 0 - } + scrollToCurrentObjective() + RxBus.send(EventObjectivesUpdateGui()) + } + holder.unFinish.setOnClickListener { + objective.accomplishedOn = 0 scrollToCurrentObjective() RxBus.send(EventObjectivesUpdateGui()) } @@ -309,7 +311,8 @@ class ObjectivesFragment : Fragment() { val progress: LinearLayout = itemView.findViewById(R.id.objective_progress) val verify: Button = itemView.findViewById(R.id.objective_verify) val start: Button = itemView.findViewById(R.id.objective_start) - val revert: Button = itemView.findViewById(R.id.objective_back) + val unFinish: Button = itemView.findViewById(R.id.objective_unfinish) + val unStart: Button = itemView.findViewById(R.id.objective_unstart) val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint) val input: EditText = itemView.findViewById(R.id.objective_input) val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java index 29d1ad85dc..7082c7e7bc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java @@ -61,10 +61,6 @@ public abstract class Objective { return true; } - public boolean isRevertable() { - return false; - } - public boolean isAccomplished() { return accomplishedOn != 0 && accomplishedOn < DateUtil.now(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java index 2407594069..bd3e69c751 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java @@ -25,9 +25,4 @@ public class Objective5 extends Objective { } }); } - - @Override - public boolean isRevertable() { - return true; - } } diff --git a/app/src/main/res/layout/objectives_item.xml b/app/src/main/res/layout/objectives_item.xml index 76adaeb015..4379ddddbf 100644 --- a/app/src/main/res/layout/objectives_item.xml +++ b/app/src/main/res/layout/objectives_item.xml @@ -68,11 +68,18 @@ android:text="@string/objectives_button_start" />