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/47] 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/47] - 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/47] 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/47] - 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/47] 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 d23faf65bff66fb5775a5d030d3d7258f1185340 Mon Sep 17 00:00:00 2001 From: Philoul Date: Wed, 6 Nov 2019 20:31:05 +0100 Subject: [PATCH 06/47] 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 07/47] 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: Thu, 7 Nov 2019 11:46:55 +0000 Subject: [PATCH 08/47] re-run calculateInsulin when OK is pressed to ensure changes to notes fields are used --- .../androidaps/plugins/general/overview/dialogs/WizardDialog.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt index 552ab502e7..efe800bef0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt @@ -114,6 +114,7 @@ class WizardDialog : DialogFragment() { log.debug("guarding: ok already clicked") } else { okClicked = true + calculateInsulin() parentContext?.let { context -> wizard?.confirmAndExecute(context) } From 2319536adc9cd34a884cf50deff0172aada402d5 Mon Sep 17 00:00:00 2001 From: fabriziocasellato Date: Mon, 11 Nov 2019 09:57:35 +0100 Subject: [PATCH 09/47] 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 10/47] 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 11/47] 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 12/47] - 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 13/47] 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 14/47] 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 15/47] 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 16/47] 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 17/47] 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 18/47] 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 19/47] 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 20/47] 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 21/47] [#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 zQ(j>(Fc<<=U4E+E(apyQo$JWGTY!Nc01Vc3H){;Tp^3bue$8%vJuZGsJsLb!ZwTGx z=WVpXFaUtd4jEN5>M5%2uWkuwR8aJFweU)5mrS#e z+Wo%%b2q;QW|E-(?_J@c*E7#j$?I3&IHZ`WCy64MLA4$b9mv=bUMHixo)^430F{&t z|137hJ97A4VqC>rW>+jy3F2sS4FA6mlzn)0g?f$nNKXXS^t&D4LYc^x58<|25{ze!`~nE1T%P zqa0@d|8e@@OibY#T%RFxVE=O71cIWB@VaBoYG z&5?csSh+yPQ7d^4o$I}Le8=Ha#A9B1ZSB?$&8*WsAQ@B4BYewULR0qr-Niq*LoV9x?f)d9mg|_c{youk$l$L6eWQKY^u$9U(1R?N+Vc@3i za_QYX-@aRFt~1E^yD@Nh6|OLSmyH!2^;TmAp0o6?|3v*6x?{5iru26!wa~i1#CAfs7d8K_YYt@vQ#BF=i z_-OmuM_*{Dkghe^!F3n;xTs=XXtOV&GHn5Q%E9o_f{=uo>sb?Z=1`pszpDIV)sksE zYp=Nu9_>f+_9%FoYFHWkz^YVNZE6l%`>JI~t^pd=AVV|#=C2?^O*7x&pB3{Y3K1h* z?8}!c3nag=p@BRFDPwdBQksF|XyJ9|z8p!z_|?6X#FSo|-YR3ZFV(Ar@h9L>em``i zPWDftTh!mO=<_=9@!PNL74G!$Uy%x_>=ImsF-9RxwVf*J-F(JZTPGP3JXJLG+L?*z zp7h>4hRy=%pvdC%2pN&Y+oqTqG$Qk_6AH|zt`wF3^=VfGk=*e$>?7;eG2}srZQ`93 zKQwg2&DGKlz1GKnLt<(S&)$^IGl!FL0Jan;_^evEaXbs_S7@S5ExOz)lhk}swF;BF zu{ySLM{-Cq=IpPd%uufht&LgbvXg6YI$9;LIQ!Flf#ky5o(^x5BGSZ%l7x??6cp0jKXmv-F!Nk{&u9(0`~R=QtIL7(Htk0)G2P&!e7V- z`$84lkPIOJ7hc%#`OXaV{L6Nh5lVY|;dW$+g;ymUfwtaYcPbF>4Ae>8slvHn4li6- zS&}yuLp7-V2|BOc{=B<$r!Z!yVJOJalsSv#Obx@wa${bm51sv1;M<2X3wh!K(fc)` z*sw?jp%IG8#!f;mAP9!qs(W3SJ%!qO%12Ca#RMDiIE0Zcqj0)NVl1an0}ZH?*|dC` zAqJoZ_>WtQ3gyrEnrUHs`(x%ppC0{*zQj6N#K-+Y?Y6a21K`ze9r|suq#wF$W#Ht= z1PY#GueRTWW+oW1C!Y{QHI$51xvnj`uzH4#vsaW3PQ5#yB%@LlR;=9QiUWH9$^3Hv zbm6m2>t2quduCM+^Lbl$`03RVn^N;efdDF#-OqClYzC6%0*)% zWV$vgs)$b=jb%yGsJe_#qbgyMGvApUvg&xgqu!57Sj)7=bLF&4-|<*DI%^}qc^UQ1$nUm%oDtNKD+cimeC`69cd;7dVMz=n`q^i6zQQ+ z^$a)k@s;)0kZplMF8&Ae!QbN4sw|uMS?DhBdgGC*%W7dM*wDhxA}F}R!`TsBvvKFP z=C&#L)=7ODpR|6bnND*)q#=4zq6GZcq@eGP^q7+z3bM>p*_dTqRBG>WZaRhDZ%`Yp z=jc1lAE~(|zq77(j%faNo2#4UnA&Ehdie0yNZ)4@l$w9h%zVu>)ZLA(By$Cls}(JN zl$gGqCw5aqEZ3Xj8G8>27b{2}e!fBrZ2h{SlM|WzD|HzzBuE}MQF$JKR0w&9`aZPm ziGcrdDP`NGeq0#D!XwH6o|m7PUF*%!>HV-;_s>1!1lGn~UQT75%1DG6*f(L27w$G>K5dTX@JI|JKBS`C& zs4}X7-D@i|16PVnrn#{Z+TkglHfb@6iJg*J4S^rOBUlEo7=-PUggjiB%t81?=qfJNUVm8Y4kC{?EGw3nx~B2z_m2iZ>~Zr|4da{ z<5GvxFzrMmU8U;!!_U(UrNG7y!{?oVP$I=5(Y zq(0fDIWJovMQ?JHb?FXRPnAktNxyx8ZqPM~5(GdNhua&EFaQZiMOxglyYWTLm zC5}l?0EUi&0u3F493pz z(6-NFH-&@wSqdL99v*le^@s%6eE|?8jva1Xz%?ad774&HJl1dts3HIY*rRybw(o(w zVfhqXKBM2Dk;S|tdR%r_14k{=uj>uW=YvU94oJZ5L*&db)8`*i#Y=^nd=qGgLt^p4 z7_UAqfaPgCc2|a(P*`L_1AN%X!1?(D^x>hsJ@U9Rf|B7J(adVPSaKd6o%bCYDM9Ux z_B8~bse+9$uI!9>QVGH08{h|4%fT;g>uKo>pTfVJBV-$sBucbiS0*q{RS@U}>DS$O zzV;;g7B=9XN_N->tK`aQwnnVM#%7^^eM{yDL8s#>;xq|!8gT*HX(e7nqGy*F9*$*} z=Zf!j%xb`e-KJY(_T-IBx9etJ8y)0Nv`LI{ILg(Ps!-|}yuZI?*dXZFG2Sw*A-|XS zFC3w$5oiRDEy^Ck3bl&u{c|va5m9zJm>%{f4rxtVL|{%m=4jLS8@pi!dFl{!cOyf3W>b&3 z=e0#`68_ZP@+z9JnOWy4qS?wr*)5=oD<~p-gS;4mJSK!{WrNCw7ls_&_PvSfVyAF{ z^25C^c~?WMO^dw0ufXa%ugQ&tyNqx>Y}2~?`8$;N7G+#w0vN;*Wz#l?wtO#ZSOZY2z8YHC(CbN zgR>SMX}i{{8rJI&TTP@0a&zPXu~Geieb;I%bZ^6(=(74Hx|Pn7v`qK`mCX~4j!cB;zk4l^#T_=|aM)bZmb4$_^1rZI)me!A57)hp4LcYfMTFjrE2>EL0(2nXVhR zhfoWv`*m3qH6jH7s=V29h2VKv13iU>#%N|`&SAY#|9a@T^5J{sNUM!TcD5%=FlY=5 zy4h|Y@6^J#xr&aY8_45|t!*!TAd>sNY#h(1Z;#(o+!&o&ZE1$O(uP=+b?EvBFb6nwH5iY_HwaJ`gyWAz2UB zXvW>r!S}yqH-T=V+46MS182x%ETDB$Ay_Ttn~OVW!8*C(Io zo6nWm^`teq>cLH_3V^)dxiI_l1y0lV&Ojb_3QRlBBOjhW7>?!aLcsu5r2eo1JIR!6 z0qMd%^g^K4n+B-2wclR3*fXWyAF$f3PRo!OmuPFy?g$4GI!t3e!o?a+2hdYA;D=4r z2zN)z59m6kxrdyqyyn8}rq7MpCU{Jo@g|1jVmE+1(sRi)jH(4C+$V|L9I$f_We}5@*M(hD(SCO=T&hK=A{~2w=^M6G!Ud zFj!;1E}@xWRSo@KXF)a4g>|&g{&Oos3VdTJB_P;*}EO~!9m*5e#u?J9_+NYDT48OfmBZ-_#{ zQsN+A-!GZ7kTfO^IKsR7F|fol`hnP}$^3?ULlopkd0s-(Ng{>TkDYxW8+N*-JRX>i zGV}TJ7YM`53oab7We_wL?vQ}mUW>mpN7MpXrD^bld}U=E!`|RV;6o$y#ti-qBb@T{ z*&5CM+WG<32?j-r(R?r;J_~1EeO|ctyOg4NEX)>I=kRjJ(UTpAO8E zexF09Po}XkZDMJdBMj5UrYM?cR$$SzyWLzwnC92g>z3W=KEk8go5&h1eci|FgY{J( z_IbV<37f%r2W1+t2D!j{164N}Vga+9{d*9`4&-$M2GIjA?iNixO5Q7I?c3%uW&_iL z5~_DKBG=>pqU>`=JjV29h^4yNG;~vkI$1m)@n08hv#`R9+kL-IRp^F)x&aAF}|4j zlK;XB?ih%!{!6S8eF6yUM(poU6Gwl2%5St=>K&q2?%QtcLJJ?K3trC`W9&WYYm(h} zr!*J!!Ws`E`8?{E9O1aJu>0lfS7V@@Pe~%UA>kVH2-q=6irwDZ{F00^s5;}#WL(4n z87!Y6P<>*Ia=~{dLsD__RwNE$-n*Dm7?j8(=UdM5s%@;TlC2h-Z6Ue z$D*Vi;L;gvF2H0F@fX9l)$^bCMG|hj{gb}mUXsg`B*xvRG*F*9$xr8;!|Mc}!Nv%d z)vUBljP9F1BUle*F}yZee-e>*)ih-VrJ6ooKIiIH2piNdURs*xWvtQY>f{bOYn&pri0?2 z1X-ltMCPAYPta=er-Q9YQekz33_f?LkZ}c>c8)L@kOkAE{5DM? zyBY{OFKM`p`D!^q@~V4L8B#C@`6EtzpJ-tJgm3MaBYnIm7QH2zDtY|w88Y8;X;|m6 z*l^_v>?M8fnsF4);;qgKjgkHE?l6u5+%K;GLV}J$1@c&@d1r-bZ_XnP)Z}Jc;VQ)- ziX)}>chV{>$i@Bo-WY>9`h^|qnpsMbbB)M;dt3x+QnE3i+k?gR?toM?Rgi^k`}*0;4ap8WheyieYIVB;75#; zq!lZ%)L+c<+l3(M!LI8ny9;(0X!>Q(eb2Olu}T!)ZUkTbN|PZO<&p6xr$ zo+I3XHFj0#Hpl`NR_T^t{L*FZD90P$iICBSx>Q0!lIvl`uU%p%Ccf-oGb?m^WPka4NIiy0#7}{xPOSW7;WF^T0DP@bj)hpfq1%>)ZIZAk1NAws>qs>;U5=dI z{8YE&1l;wDW)haD%{X0b-8s#Y_FdmS$n|F32Hp>Wg*S+AYxK@w^%!sBQPUPjn$M-f z+T{!jD%jV*p2#zioPe!jgi6tD7Hk;U@Bjw@8072tli+k~-M>!Ey6kyMH{agm0WkvfdSjwpl^lFNrG9dH=s6uyBR#Sb4NNxd+D6df!% zTs*t&hqb^bQ>A9to(t%0Dzu7BIg3>zX^1CQzvGG#1wwSPdb>N zWyyb|xWQOyZJWnp2N$$~U^~Q!SGY!z z8jM?F=dmsp+#nph%`&NiJQV&haL5J85IMsl)$lnid>Ao)edq!z%iT%)i!|~^%%A>==9e?Dn&<1-yP*SDf#p;HXwEI%G!mF zy3>^v0%;2PHQd9)n6eG9mnLRtII%%VMdX+rwnvQTxTR(^&xurrqXa|k4AEA7C!R{P zH#iOM*(5>V!qZ6@e%L;syMy8;nJ$fJWe-QR)5RGI)b`P&%J=tY;)L(!{u=`wOQFA_ zsNc>%%RE)4;7xQaLoo6K^x8T&;_X7NU`n)GYBKxvGVEfiO4+Ss;=)oYK3qDPA-X_( z5n_GeD0y+OTZt<(6|-L)ZYbQXDupAV5|Z#7a@fOdGV4URm=Kzp*-O)Y8-&~o|4sd& zlaxwtnz8+4AMT>)+}#!Ud|GKWoR^Sr$ss@A5tojy!hM&Mf&$TU!Ef0a;z(*rB+XnM zy;swe&*9gWvCj}UF~#_OdGG^&8f6=#*_~765DrJb1+#6xdj`woqz#hQwZq{!ae}l_ z4eph11VhbI73+(-8gz^J4tA6MZ7lWX z2ptTSCy!cTCCt(YE5$|A>NE}){m$PNRHZWeS8hq-V_V=LOIjjB@cPj%y7I>dP=jG% z<_tmb?(Bd?v`ZY~+53!FZ{$-fL=$$VXtd61jJY9lE;9t%taNi4`M^P%2Ck%82X7^y zwS*hA%yOq?h?zPw1tkR-T2EUV0BVz`QYT8MJ99J&$8k1spKv#SFcpgU)!U|6;)M}O z__RreGUiVJ3KzSBL}77Q^%2%IMELqugC(}J?gwNR8RfzZSA5xsrZXgBnE!6KX12Kx z@>Dy}l%F4tVg3pzJ*{Fc}MFb3dhN6x4~A z2@0~+ks==DXrzwp7o8zQqe@5m@bp@^gObyNDiA`^dip1f``7F#vaFIK~JD zi3r0?80&se#}%^;nSz**KwiR)sTM7BF!#fK_v8c9V~o>K+tPEYmd7WG#Ccj5N(0Uo zrv^oWp7Ac9dx?GOpfJTZfE=gkzuP2!~>~vQNY6mIv0-97Nqk~v zwHacLF>Nsrjn7c)J5(}u=uzt`mJlJ8I4!~q+5b_DCPjro0h+CGXJ{G$y!J++wGMb% zEsv^2SuVqGkk{$awHX4yiskD0caa*7QY?b$uNHQZI72KPgUa+$9?e{u-740OCQ%`f zK`y*VMnLMRNfuvcha!u|7;8>jfZb`8`fb1*b5aCU=3Qc{dT(z*1b}~ENH@+V^f^BoMe&X1Sy~j z;KJXrj*)3PI4jdew8{-BF;&(=un_5N*@DliWY_@I9j({Di}zaxh3cpw0Q2@xH!+d6bLW_Jt1*a;M@Ng@Wz#p!t}mIr|W_CsG_n$msQD4z8c+EgQG{ zJm@{^#v6Srwj8yCap5@yBiMGiFlX#tvG{C6(PfDtXr(@dgi&`YP`kH;W7zd)VERMg z2v556Jl-z`@?g;QpYb@-c3aQm#Stn{Ao)TJu}&;fC|Fvl;fkX(xUj;EO(lnXW)#OB zrr=A#ccz{>MgHehlBgEW845qgjhLf@qS(TvX~&$tgIgnL=L{Xi)>yYrqO@C(y)$ym z2HGhMn=j!K7BD!W1F`FbVrGb$d#lf2dT27tJNn_h)h?K3UKp%#tsbfgCj>fUmQyJW z-X)UJY_SD27d>QY2oMIqnuvI4iTT(zAACMT4Y$9Ws~}j-3^jZUxkkk@DL}Y$1TtE} z^{cW-najA^BH_PTP6V4pDkdS*d4lB$5wb%M5R(l=F&Nne{l9X5ly}9%ESH zfNAY1;*}$5P9()s!|?v7K+f(uEJFF3s|MkxJ`zN3sxIO7E^3VV&Jo0N<_YdJSEhm0 z$6IU6f(;*n-qjj8dp1nvBGy7pg2ER|(T7xGMUhZ2Lwz4Eq?^=-&+p?#?#|( zPz>19i<{}xg8gR51w5u&gsX)`Vh%Nv3{~u%kxH#c>=oRwd?iHpBQLGMG9RW?IlD03 zo+Uq;!M~5;l!KmM!R_zkK0=e^C3L3K{)B@uZ68~tOrm2Sv0 zSl}h7#nBX#_yBM(M@yQgj2Npua-RpwYz9w=h*uKM4{{8o-DkSZ zwX3QPN);+%6+Pq_9*_7Al7gk7QuNk-tdF*kq3{xQtOL9d__jS{tjXaS>E%2QXLbxf z70kntM-J+;$lqDm7Z3&p^rR8_0fuw{@xwgc_8BqD6>(ocT@i|CIzu%8cEJMLwLY%e zZro=`LARNqyj?pAjqKy%U^oveCSZ<7BU||3oAPn{qM{h`Lorm*LS@D6)&~S&srNr1 zPQ`vq1VX`r8#Xy8W(!!?FW3$^3M{e<;?pe*aLOEobMv?cEL-j15V}eJ0F-a{`HI9K zdFjX=%=TGi&v|?e7A?0SEasqiEJ;zocKa@XWHW>Yp%2)MVb!#GfMpDYTMS9;QP{R9 zYz|P}?RpOYK7fe*3HJe*A;-{W5x)(>dvI|sU?GXH3L!zD5#3&#M+|;MK$n4EgM0uE z`#g4kY#lJbjBJNA3`S$`d&C6#fT!s!Z9fnz`7pF|X%=c#I8%VN8F0%(r*M8l*j^Vt zgo3e3nih;TG;g9b`ZGAqSW?IZsa%HEpr=;6wa`=ml7KUHA|ktT&}8#q%F->CFii@$ z3_;@asKp4tVOwknx&KcU*B;x{b;iGQeEhg^92}3!#L_4I?}PgQOU_{ezX+_;!4 zDSI-opyX!t{;auW?Ru9 zje!Zx$9bt^)CD@X5A*Z&&8(9-D+al{y7(~F48_WC#&UP9w{Iwu(qdNLjt-+vtnP-B zILCORVnCRYUT)OZOufjet?;!;!ej;c#teU}UyIGSOKd`Xd?MR*y2pi}Lx!DO)Up&} z9Qbs+op+p3MgKN`+nM5*uCqRJWVD#yyD`H$;{jWSZ_sD#U8B0`=fFWoqHmaA^lBrz ze$|xNUE7lt1#YpW<#q#m^KIuHeD1!%hG?C3hyUzr3iJ4$r)_CcD~uUdtNbxuizKQdfc0 zqEm4y=}?h(x|JQHJqeaU$~0^jYRhZix=_*$h!nd;Og7so{!vh8-JmGsQ9nLEL!qAJCu2S)= z;F0-48p{js^UmnXBxy_j!4{ONNXp_OszBS2+?W@$C0r8-W8pGE%Sx`(-{f=7-S_GI z1+AnpD0Yr!nfRi6XIOw`g>2^OemsANnWDDGt|oH9GJ3+SLP*tOuY{wb{jj>|Y+wHQ z4>_wiioZVzetgDUe^Bb2|M;IRZrS_Z{WJ8-aiQL+G<)(-_jqXBR<8$@-^I-c3y3;A z=g>-bSS10f!U8KB*;vatwb}LoT9=&pNv&DDC~L1q@4SQEf~VN_VJqsa~>Ys3q$P89{LfX_>5b-+w*2T6O`X>$jM1ewx<(o)&Q} z1{D^c;z&(0%5f_o4UWo$>A-GuKWC~36=1v&`5Nlj>3)H$F7gtK6?VuU7?ejc?6P9& zYkhes*9xiAvAf)SvUagi(g7PaxM#3c%h)|vheSXeXR)G`v)O!m#H#=AR*@Fi(1efS zmXw&%=8*{EmFY*un=og39p<8Dj%-6-5?UU{Lr?|jLbY0ZQFq_rWu#t_&R8y6PvTE9 z+XYK0HgSVqD0;ceeOoW%r%rvCWy*yEFQI$&+5K^d5yI|v{_1UZ`JwtAMi_MwQ>0_#LriK?XSM!8KE$8u#@h(??+$%QZy0}U!UWQ2s6 z-OAq>gfkFOhlcL*r`NGj)TndreR9LL{GkO(&I@K9J46rE26+5AX^T7pn^WX$L;E#N zur{kZ$l2y4yJ-M(R>Ra{GYsFVNVFvGnBZ)_S!K-v^=^k|+$I`spp)i~eOJxr+u6za zq7I1FmD`oKJUw}Hn-L1qB~#Fm3b>pcejxTY!=l8LI)RnI zRe}`?+~y17wzXsYU#;BG9Y^{wEfQ`$Z^%SyV^>X;)||*?=T>WfH2hv;-sgN(6~EYG zlauF0|2;vE3Gsl7@qfeRF!3u5q~_r70r)GiT)swG)@yf~g0z3P5q2dT)uF?~(a=*f zgZ7Q9=r(^u{VG3N~)2A;p%=c_fe!=oD0)|q+d|Ehgvt>Z{Pa$_vXfZ$*l z*317!5KJ-PAzcR+d9!)ilVUM1jGpSz%FJ(f`F?bHjQGZQ{s>B(DJ?p^`Kqc=zbZSp zDVE3I+w+hW48SGnzQvzUvNZ(1WEwPUNs}(;Cfz$+qSO?^zjl2ynC^F@ z(Vna}ze=+fX&oEf$!w}(3z-U?aUYb?x& zF3(FJq1}hBeSuZ$Lt?;IAA0;VG!_%nr>84%*jTF^A~`(|1Bdz;2uor%p x>V&&H-%<_;H&h&~Mb&@~jH@@WA+@XAsLE>Ux0kBHIe!lY>@UpY`Y*n|^dH*5KIH%a 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 22/47] 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 23/47] 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 24/47] 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 25/47] 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 26/47] 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 27/47] 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 28/47] 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 29/47] 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 30/47] 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 31/47] 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 32/47] 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 33/47] 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 34/47] 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 35/47] 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 36/47] 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 37/47] 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 38/47] 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 39/47] 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 40/47] 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 41/47] [#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 42/47] [#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 43/47] [#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 44/47] [#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 45/47] [#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 46/47] [#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 47/47] 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