Merge branch 'dagger3' of https://github.com/MilosKozak/AndroidAPS into dagger3

This commit is contained in:
Milos Kozak 2019-12-30 23:27:01 +01:00
commit 040d999004
14 changed files with 910 additions and 1134 deletions

View file

@ -162,6 +162,7 @@ public class MainApp extends DaggerApplication {
@Inject TreatmentsPlugin treatmentsPlugin; @Inject TreatmentsPlugin treatmentsPlugin;
@Inject VirtualPumpPlugin virtualPumpPlugin; @Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject VersionCheckerPlugin versionCheckerPlugin; @Inject VersionCheckerPlugin versionCheckerPlugin;
@Inject WearPlugin wearPlugin;
@Override @Override
public void onCreate() { public void onCreate() {
@ -259,7 +260,7 @@ public class MainApp extends DaggerApplication {
if (!Config.NSCLIENT) pluginsList.add(smsCommunicatorPlugin); if (!Config.NSCLIENT) pluginsList.add(smsCommunicatorPlugin);
pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(FoodPlugin.getPlugin());
pluginsList.add(WearPlugin.initPlugin(this)); pluginsList.add(wearPlugin);
pluginsList.add(statusLinePlugin); pluginsList.add(statusLinePlugin);
pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(PersistentNotificationPlugin.getPlugin());
pluginsList.add(NSClientPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin());

View file

@ -74,6 +74,7 @@ public class MyPreferenceFragment extends PreferenceFragment implements HasAndro
@Inject StatusLinePlugin statusLinePlugin; @Inject StatusLinePlugin statusLinePlugin;
@Inject TidepoolPlugin tidepoolPlugin; @Inject TidepoolPlugin tidepoolPlugin;
@Inject VirtualPumpPlugin virtualPumpPlugin; @Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject WearPlugin wearPlugin;
@Override @Override
public void setArguments(Bundle args) { public void setArguments(Bundle args) {
@ -157,7 +158,7 @@ public class MyPreferenceFragment extends PreferenceFragment implements HasAndro
addPreferencesFromResource(R.xml.pref_others); addPreferencesFromResource(R.xml.pref_others);
addPreferencesFromResource(R.xml.pref_datachoices); addPreferencesFromResource(R.xml.pref_datachoices);
addPreferencesFromResourceIfEnabled(WearPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(wearPlugin, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(statusLinePlugin, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(statusLinePlugin, PluginType.GENERAL);
} }

View file

@ -31,6 +31,7 @@ object L {
const val UI = "UI" const val UI = "UI"
const val LOCATION = "LOCATION" const val LOCATION = "LOCATION"
const val SMS = "SMS" const val SMS = "SMS"
const val WEAR = "WEAR"
init { init {
logElements.add(LogElement(APS, defaultValue = true)) logElements.add(LogElement(APS, defaultValue = true))
@ -58,6 +59,7 @@ object L {
logElements.add(LogElement(PUMPQUEUE, true)) logElements.add(LogElement(PUMPQUEUE, true))
logElements.add(LogElement(SMS, true)) logElements.add(LogElement(SMS, true))
logElements.add(LogElement(UI, true)) logElements.add(LogElement(UI, true))
logElements.add(LogElement(WEAR, true))
} }
private fun findByName(name: String): LogElement { private fun findByName(name: String): LogElement {
@ -145,5 +147,6 @@ enum class LTag(val tag: String) {
CONFIGBUILDER("CONFIGBUILDER"), CONFIGBUILDER("CONFIGBUILDER"),
UI("UI"), UI("UI"),
LOCATION("LOCATION"), LOCATION("LOCATION"),
WEAR("WEAR"),
SMS("SMS"), SMS("SMS"),
} }

View file

@ -78,6 +78,7 @@ public class LoopPlugin extends PluginBase {
private final ConfigBuilderPlugin configBuilderPlugin; private final ConfigBuilderPlugin configBuilderPlugin;
private final TreatmentsPlugin treatmentsPlugin; private final TreatmentsPlugin treatmentsPlugin;
private final VirtualPumpPlugin virtualPumpPlugin; private final VirtualPumpPlugin virtualPumpPlugin;
private final ActionStringHandler actionStringHandler;
private CompositeDisposable disposable = new CompositeDisposable(); private CompositeDisposable disposable = new CompositeDisposable();
@ -111,6 +112,7 @@ public class LoopPlugin extends PluginBase {
public Date lastOpenModeAccept; public Date lastOpenModeAccept;
} }
@Deprecated
static public LastRun lastRun = null; static public LastRun lastRun = null;
@Inject @Inject
@ -124,7 +126,8 @@ public class LoopPlugin extends PluginBase {
MainApp mainApp, MainApp mainApp,
ConfigBuilderPlugin configBuilderPlugin, ConfigBuilderPlugin configBuilderPlugin,
TreatmentsPlugin treatmentsPlugin, TreatmentsPlugin treatmentsPlugin,
VirtualPumpPlugin virtualPumpPlugin VirtualPumpPlugin virtualPumpPlugin,
ActionStringHandler actionStringHandler
) { ) {
super(new PluginDescription() super(new PluginDescription()
.mainType(PluginType.LOOP) .mainType(PluginType.LOOP)
@ -145,6 +148,7 @@ public class LoopPlugin extends PluginBase {
this.configBuilderPlugin = configBuilderPlugin; this.configBuilderPlugin = configBuilderPlugin;
this.treatmentsPlugin = treatmentsPlugin; this.treatmentsPlugin = treatmentsPlugin;
this.virtualPumpPlugin = virtualPumpPlugin; this.virtualPumpPlugin = virtualPumpPlugin;
this.actionStringHandler = actionStringHandler;
loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L); loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L);
isSuperBolus = sp.getBoolean("isSuperBolus", false); isSuperBolus = sp.getBoolean("isSuperBolus", false);
@ -471,13 +475,13 @@ public class LoopPlugin extends PluginBase {
rxBus.send(new EventNewOpenLoopNotification()); rxBus.send(new EventNewOpenLoopNotification());
// Send to Wear // Send to Wear
ActionStringHandler.handleInitiate("changeRequest"); actionStringHandler.handleInitiate("changeRequest");
} else if (allowNotification) { } else if (allowNotification) {
// dismiss notifications // dismiss notifications
NotificationManager notificationManager = NotificationManager notificationManager =
(NotificationManager) mainApp.getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) mainApp.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(Constants.notificationID); notificationManager.cancel(Constants.notificationID);
ActionStringHandler.handleInitiate("cancelChangeRequest"); actionStringHandler.handleInitiate("cancelChangeRequest");
} }
} }

View file

@ -141,6 +141,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList
@Inject DexcomPlugin dexcomPlugin; @Inject DexcomPlugin dexcomPlugin;
@Inject XdripPlugin xdripPlugin; @Inject XdripPlugin xdripPlugin;
@Inject NotificationStore notificationStore; @Inject NotificationStore notificationStore;
@Inject ActionStringHandler actionStringHandler;
private CompositeDisposable disposable = new CompositeDisposable(); private CompositeDisposable disposable = new CompositeDisposable();
@ -968,7 +969,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList
(NotificationManager) mainApp.getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) mainApp.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(Constants.notificationID); notificationManager.cancel(Constants.notificationID);
ActionStringHandler.handleInitiate("cancelChangeRequest"); actionStringHandler.handleInitiate("cancelChangeRequest");
} }
private void updatePumpStatus(String status) { private void updatePumpStatus(String status) {

View file

@ -1,758 +0,0 @@
package info.nightscout.androidaps.plugins.general.wear;
import android.app.NotificationManager;
import android.content.Context;
import androidx.annotation.NonNull;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import info.nightscout.androidaps.Config;
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.BgReading;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.ProfileSwitch;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TDD;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
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.ConstraintChecker;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump;
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.BolusWizard;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.HardLimits;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.SafeParse;
import info.nightscout.androidaps.utils.ToastUtils;
/**
* Created by adrian on 09/02/17.
*/
public class ActionStringHandler {
public static final int TIMEOUT = 65 * 1000;
private static long lastSentTimestamp = 0;
private static String lastConfirmActionString = null;
private static BolusWizard lastBolusWizard = null;
public synchronized static void handleInitiate(String actionstring) {
if (!SP.getBoolean("wearcontrol", false)) return;
lastBolusWizard = null;
String rTitle = "CONFIRM"; //TODO: i18n
String rMessage = "";
String rAction = "";
// do the parsing and check constraints
String[] act = actionstring.split("\\s+");
if ("fillpreset".equals(act[0])) {
///////////////////////////////////// PRIME/FILL
double amount = 0d;
if ("1".equals(act[1])) {
amount = SP.getDouble("fill_button1", 0.3);
} else if ("2".equals(act[1])) {
amount = SP.getDouble("fill_button2", 0d);
} else if ("3".equals(act[1])) {
amount = SP.getDouble("fill_button3", 0d);
} else {
return;
}
Double insulinAfterConstraints = ConstraintChecker.getInstance().applyBolusConstraints(new Constraint<>(amount)).value();
rMessage += MainApp.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U";
if (insulinAfterConstraints - amount != 0)
rMessage += "\n" + MainApp.gs(R.string.constraintapllied);
rAction += "fill " + insulinAfterConstraints;
} else if ("fill".equals(act[0])) {
////////////////////////////////////////////// PRIME/FILL
double amount = SafeParse.stringToDouble(act[1]);
Double insulinAfterConstraints = ConstraintChecker.getInstance().applyBolusConstraints(new Constraint<>(amount)).value();
rMessage += MainApp.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U";
if (insulinAfterConstraints - amount != 0)
rMessage += "\n" + MainApp.gs(R.string.constraintapllied);
rAction += "fill " + insulinAfterConstraints;
} else if ("bolus".equals(act[0])) {
////////////////////////////////////////////// BOLUS
double insulin = SafeParse.stringToDouble(act[1]);
int carbs = SafeParse.stringToInt(act[2]);
Double insulinAfterConstraints = ConstraintChecker.getInstance().applyBolusConstraints(new Constraint<>(insulin)).value();
Integer carbsAfterConstraints = ConstraintChecker.getInstance().applyCarbsConstraints(new Constraint<>(carbs)).value();
rMessage += MainApp.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n";
rMessage += MainApp.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g";
if ((insulinAfterConstraints - insulin != 0) || (carbsAfterConstraints - carbs != 0)) {
rMessage += "\n" + MainApp.gs(R.string.constraintapllied);
}
rAction += "bolus " + insulinAfterConstraints + " " + carbsAfterConstraints;
} else if ("temptarget".equals(act[0])) {
///////////////////////////////////////////////////////// TEMPTARGET
boolean isMGDL = Boolean.parseBoolean(act[1]);
if (ProfileFunctions.getSystemUnits().equals(Constants.MGDL) != isMGDL) {
sendError("Different units used on watch and phone!");
return;
}
int duration = SafeParse.stringToInt(act[2]);
if (duration == 0) {
rMessage += "Zero-Temp-Target - cancelling running Temp-Targets?";
rAction = "temptarget true 0 0 0";
} else {
double low = SafeParse.stringToDouble(act[3]);
double high = SafeParse.stringToDouble(act[4]);
if (!isMGDL) {
low *= Constants.MMOLL_TO_MGDL;
high *= Constants.MMOLL_TO_MGDL;
}
if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) {
sendError("Min-BG out of range!");
return;
}
if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) {
sendError("Max-BG out of range!");
return;
}
rMessage += "Temptarget:\nMin: " + act[3] + "\nMax: " + act[4] + "\nDuration: " + act[2];
rAction = actionstring;
}
} else if ("status".equals(act[0])) {
////////////////////////////////////////////// STATUS
rTitle = "STATUS";
rAction = "statusmessage";
if ("pump".equals(act[1])) {
rTitle += " PUMP";
rMessage = getPumpStatus();
} else if ("loop".equals(act[1])) {
rTitle += " LOOP";
rMessage = "TARGETS:\n" + getTargetsStatus();
rMessage += "\n\n" + getLoopStatus();
rMessage += "\n\nOAPS RESULT:\n" + getOAPSResultStatus();
}
} else if ("wizard".equals(act[0])) {
sendError("Update APP on Watch!");
return;
} else if ("wizard2".equals(act[0])) {
////////////////////////////////////////////// WIZARD
Integer carbsBeforeConstraints = SafeParse.stringToInt(act[1]);
Integer carbsAfterConstraints = ConstraintChecker.getInstance().applyCarbsConstraints(new Constraint<>(carbsBeforeConstraints)).value();
if (carbsAfterConstraints - carbsBeforeConstraints != 0) {
sendError("Carb constraint violation!");
return;
}
boolean useBG = SP.getBoolean(R.string.key_wearwizard_bg, true);
boolean useTT = SP.getBoolean(R.string.key_wearwizard_tt, false);
boolean useBolusIOB = SP.getBoolean(R.string.key_wearwizard_bolusiob, true);
boolean useBasalIOB = SP.getBoolean(R.string.key_wearwizard_basaliob, true);
boolean useCOB = SP.getBoolean(R.string.key_wearwizard_cob, true);
boolean useTrend = SP.getBoolean(R.string.key_wearwizard_trend, false);
int percentage = Integer.parseInt(act[2]);
Profile profile = ProfileFunctions.getInstance().getProfile();
String profileName = ProfileFunctions.getInstance().getProfileName();
if (profile == null) {
sendError("No profile found!");
return;
}
BgReading bgReading = DatabaseHelper.actualBg();
if (bgReading == null && useBG) {
sendError("No recent BG to base calculation on!");
return;
}
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "Wizard wear");
if (useCOB && (cobInfo == null || cobInfo.displayCob == null)) {
sendError("Unknown COB! BG reading missing or recent app restart?");
return;
}
DecimalFormat format = new DecimalFormat("0.00");
DecimalFormat formatInt = new DecimalFormat("0");
BolusWizard bolusWizard = new BolusWizard(profile, profileName, TreatmentsPlugin.getPlugin().getTempTargetFromHistory(),
carbsAfterConstraints, cobInfo.displayCob, bgReading.valueToUnits(ProfileFunctions.getSystemUnits()),
0d, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend);
if (Math.abs(bolusWizard.getInsulinAfterConstraints() - bolusWizard.getCalculatedTotalInsulin()) >= 0.01) {
sendError("Insulin constraint violation!" +
"\nCannot deliver " + format.format(bolusWizard.getCalculatedTotalInsulin()) + "!");
return;
}
if (bolusWizard.getCalculatedTotalInsulin() <= 0 && bolusWizard.getCarbs() <= 0) {
rAction = "info";
rTitle = "INFO";
} else {
rAction = actionstring;
}
rMessage += "Carbs: " + bolusWizard.getCarbs() + "g";
rMessage += "\nBolus: " + format.format(bolusWizard.getCalculatedTotalInsulin()) + "U";
rMessage += "\n_____________";
rMessage += "\nCalc (IC:" + DecimalFormatter.to1Decimal(bolusWizard.getIc()) + ", " + "ISF:" + DecimalFormatter.to1Decimal(bolusWizard.getSens()) + "): ";
rMessage += "\nFrom Carbs: " + format.format(bolusWizard.getInsulinFromCarbs()) + "U";
if (useCOB)
rMessage += "\nFrom" + formatInt.format(cobInfo.displayCob) + "g COB : " + format.format(bolusWizard.getInsulinFromCOB()) + "U";
if (useBG)
rMessage += "\nFrom BG: " + format.format(bolusWizard.getInsulinFromBG()) + "U";
if (useBolusIOB)
rMessage += "\nBolus IOB: " + format.format(bolusWizard.getInsulinFromBolusIOB()) + "U";
if (useBasalIOB)
rMessage += "\nBasal IOB: " + format.format(bolusWizard.getInsulinFromBasalsIOB()) + "U";
if (useTrend)
rMessage += "\nFrom 15' trend: " + format.format(bolusWizard.getInsulinFromTrend()) + "U";
if (percentage != 100) {
rMessage += "\nPercentage: " + format.format(bolusWizard.getTotalBeforePercentageAdjustment()) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.getCalculatedTotalInsulin()) + "U";
}
lastBolusWizard = bolusWizard;
} else if ("opencpp".equals(act[0])) {
ProfileSwitch activeProfileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis());
if (activeProfileSwitch == null) {
sendError("No active profile switch!");
return;
} else {
// read CPP values
rTitle = "opencpp";
rMessage = "opencpp";
rAction = "opencpp" + " " + activeProfileSwitch.percentage + " " + activeProfileSwitch.timeshift;
}
} else if ("cppset".equals(act[0])) {
ProfileSwitch activeProfileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis());
if (activeProfileSwitch == null) {
sendError("No active profile switch!");
return;
} else {
// read CPP values
rMessage = "CPP:" + "\n\n" +
"Timeshift: " + act[1] + "\n" +
"Percentage: " + act[2] + "%";
rAction = actionstring;
}
} else if ("tddstats".equals(act[0])) {
Object activePump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (activePump != null) {
// check if DB up to date
List<TDD> dummies = new LinkedList<TDD>();
List<TDD> historyList = getTDDList(dummies);
if (isOldData(historyList)) {
rTitle = "TDD";
rAction = "statusmessage";
rMessage = "OLD DATA - ";
//if pump is not busy: try to fetch data
final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (pump.isBusy()) {
rMessage += MainApp.gs(R.string.pumpbusy);
} else {
rMessage += "trying to fetch data from pump.";
ConfigBuilderPlugin.getPlugin().getCommandQueue().loadTDDs(new Callback() {
@Override
public void run() {
List<TDD> dummies = new LinkedList<TDD>();
List<TDD> historyList = getTDDList(dummies);
if (isOldData(historyList)) {
sendStatusmessage("TDD", "TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList, dummies));
} else {
sendStatusmessage("TDD", generateTDDMessage(historyList, dummies));
}
}
});
}
} else {
// if up to date: prepare, send (check if CPP is activated -> add CPP stats)
rTitle = "TDD";
rAction = "statusmessage";
rMessage = generateTDDMessage(historyList, dummies);
}
}
} else if ("ecarbs".equals(act[0])) {
////////////////////////////////////////////// ECARBS
int carbs = SafeParse.stringToInt(act[1]);
int starttime = SafeParse.stringToInt(act[2]);
int duration = SafeParse.stringToInt(act[3]);
long starttimestamp = System.currentTimeMillis() + starttime * 60 * 1000;
Integer carbsAfterConstraints = ConstraintChecker.getInstance().applyCarbsConstraints(new Constraint<>(carbs)).value();
rMessage += MainApp.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g";
rMessage += "\n" + MainApp.gs(R.string.time) + ": " + DateUtil.timeString(starttimestamp);
rMessage += "\n" + MainApp.gs(R.string.duration) + ": " + duration + "h";
if ((carbsAfterConstraints - carbs != 0)) {
rMessage += "\n" + MainApp.gs(R.string.constraintapllied);
}
if (carbsAfterConstraints <= 0) {
sendError("Carbs = 0! No action taken!");
return;
}
rAction += "ecarbs " + carbsAfterConstraints + " " + starttimestamp + " " + duration;
} else if ("changeRequest".equals(act[0])) {
////////////////////////////////////////////// CHANGE REQUEST
rTitle = MainApp.gs(R.string.openloop_newsuggestion);
rAction = "changeRequest";
final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun;
rMessage += finalLastRun.constraintsProcessed;
WearPlugin.getPlugin().requestChangeConfirmation(rTitle, rMessage, rAction);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = rAction;
return;
} else if ("cancelChangeRequest".equals(act[0])) {
////////////////////////////////////////////// CANCEL CHANGE REQUEST NOTIFICATION
rAction = "cancelChangeRequest";
WearPlugin.getPlugin().requestNotificationCancel(rAction);
return;
} else return;
// send result
WearPlugin.getPlugin().requestActionConfirmation(rTitle, rMessage, rAction);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = rAction;
}
private static String generateTDDMessage(List<TDD> historyList, List<TDD> dummies) {
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null) {
return "No profile loaded :(";
}
if (historyList.isEmpty()) {
return "No history data!";
}
DateFormat df = new SimpleDateFormat("dd.MM.");
String message = "";
double refTDD = profile.baseBasalSum() * 2;
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (df.format(new Date(historyList.get(0).date)).equals(df.format(new Date()))) {
double tdd = historyList.get(0).getTotal();
historyList.remove(0);
message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n";
message += "\n";
} else if (pump != null && pump instanceof DanaRPlugin) {
double tdd = DanaRPump.getInstance().dailyTotalUnits;
message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n";
message += "\n";
}
int i = 0;
double sum = 0d;
double weighted03 = 0d;
double weighted05 = 0d;
double weighted07 = 0d;
Collections.reverse(historyList);
for (TDD record : historyList) {
double tdd = record.getTotal();
if (i == 0) {
weighted03 = tdd;
weighted05 = tdd;
weighted07 = tdd;
} else {
weighted07 = (weighted07 * 0.3 + tdd * 0.7);
weighted05 = (weighted05 * 0.5 + tdd * 0.5);
weighted03 = (weighted03 * 0.7 + tdd * 0.3);
}
i++;
}
message += "weighted:\n";
message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n";
message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n";
message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n";
message += "\n";
Collections.reverse(historyList);
//add TDDs:
for (TDD record : historyList) {
double tdd = record.getTotal();
message += df.format(new Date(record.date)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (dummies.contains(record) ? "x" : "") + "\n";
}
return message;
}
public static boolean isOldData(List<TDD> historyList) {
Object activePump = ConfigBuilderPlugin.getPlugin().getActivePump();
PumpInterface dana = DanaRPlugin.getPlugin();
PumpInterface danaRS = DanaRSPlugin.getPlugin();
PumpInterface danaV2 = DanaRv2Plugin.getPlugin();
PumpInterface danaKorean = DanaRKoreanPlugin.getPlugin();
PumpInterface insight = LocalInsightPlugin.getPlugin();
boolean startsYesterday = activePump == dana || activePump == danaRS || activePump == danaV2 || activePump == danaKorean || activePump == insight;
DateFormat df = new SimpleDateFormat("dd.MM.");
return (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).date)).equals(df.format(new Date(System.currentTimeMillis() - (startsYesterday ? 1000 * 60 * 60 * 24 : 0))))));
}
@NonNull
public static List<TDD> getTDDList(List<TDD> returnDummies) {
List<TDD> historyList = MainApp.getDbHelper().getTDDs();
historyList = historyList.subList(0, Math.min(10, historyList.size()));
//fill single gaps - only needed for Dana*R data
List<TDD> dummies = (returnDummies != null) ? returnDummies : (new LinkedList());
DateFormat df = new SimpleDateFormat("dd.MM.");
for (int i = 0; i < historyList.size() - 1; i++) {
TDD elem1 = historyList.get(i);
TDD elem2 = historyList.get(i + 1);
if (!df.format(new Date(elem1.date)).equals(df.format(new Date(elem2.date + 25 * 60 * 60 * 1000)))) {
TDD dummy = new TDD();
dummy.date = elem1.date - 24 * 60 * 60 * 1000;
dummy.basal = elem1.basal / 2;
dummy.bolus = elem1.bolus / 2;
dummies.add(dummy);
elem1.basal /= 2;
elem1.bolus /= 2;
}
}
historyList.addAll(dummies);
Collections.sort(historyList, new Comparator<TDD>() {
@Override
public int compare(TDD lhs, TDD rhs) {
return (int) (rhs.date - lhs.date);
}
});
return historyList;
}
@NonNull
private static String getPumpStatus() {
return ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(false);
}
@NonNull
private static String getLoopStatus() {
String ret = "";
// decide if enabled/disabled closed/open; what Plugin as APS?
final LoopPlugin loopPlugin = LoopPlugin.getPlugin();
if (loopPlugin.isEnabled(loopPlugin.getType())) {
if (ConstraintChecker.getInstance().isClosedLoopAllowed().value()) {
ret += "CLOSED LOOP\n";
} else {
ret += "OPEN LOOP\n";
}
final APSInterface aps = ConfigBuilderPlugin.getPlugin().getActiveAPS();
ret += "APS: " + ((aps == null) ? "NO APS SELECTED!" : ((PluginBase) aps).getName());
if (LoopPlugin.lastRun != null) {
if (LoopPlugin.lastRun.lastAPSRun != null)
ret += "\nLast Run: " + DateUtil.timeString(LoopPlugin.lastRun.lastAPSRun);
if (LoopPlugin.lastRun.lastEnact != null)
ret += "\nLast Enact: " + DateUtil.timeString(LoopPlugin.lastRun.lastEnact);
}
} else {
ret += "LOOP DISABLED\n";
}
return ret;
}
@NonNull
private static String getTargetsStatus() {
String ret = "";
if (!Config.APS) {
return "Targets only apply in APS mode!";
}
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null) {
return "No profile set :(";
}
//Check for Temp-Target:
TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory();
if (tempTarget != null) {
ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.low, tempTarget.low, Constants.MGDL, ProfileFunctions.getSystemUnits());
ret += "\nuntil: " + DateUtil.timeString(tempTarget.originalEnd());
ret += "\n\n";
}
ret += "DEFAULT RANGE: ";
ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), ProfileFunctions.getSystemUnits()) + " - " + Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), ProfileFunctions.getSystemUnits());
ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), ProfileFunctions.getSystemUnits());
return ret;
}
private static String getOAPSResultStatus() {
String ret = "";
if (!Config.APS) {
return "Only apply in APS mode!";
}
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null) {
return "No profile set :(";
}
APSInterface usedAPS = ConfigBuilderPlugin.getPlugin().getActiveAPS();
if (usedAPS == null) {
return "No active APS :(!";
}
APSResult result = usedAPS.getLastAPSResult();
if (result == null) {
return "Last result not available!";
}
if (!result.isChangeRequested()) {
ret += MainApp.gs(R.string.nochangerequested) + "\n";
} else if (result.rate == 0 && result.duration == 0) {
ret += MainApp.gs(R.string.canceltemp) + "\n";
} else {
ret += MainApp.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " +
"(" + DecimalFormatter.to2Decimal(result.rate / ConfigBuilderPlugin.getPlugin().getActivePump().getBaseBasalRate() * 100) + "%)\n" +
MainApp.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration) + " min\n";
}
ret += "\n" + MainApp.gs(R.string.reason) + ": " + result.reason;
return ret;
}
public synchronized static void handleConfirmation(String actionString) {
if (!SP.getBoolean("wearcontrol", false)) return;
//Guard from old or duplicate confirmations
if (lastConfirmActionString == null) return;
if (!lastConfirmActionString.equals(actionString)) return;
if (System.currentTimeMillis() - lastSentTimestamp > TIMEOUT) return;
lastConfirmActionString = null;
// do the parsing, check constraints and enact!
String[] act = actionString.split("\\s+");
if ("fill".equals(act[0])) {
Double amount = SafeParse.stringToDouble(act[1]);
Double insulinAfterConstraints = ConstraintChecker.getInstance().applyBolusConstraints(new Constraint<>(amount)).value();
if (amount - insulinAfterConstraints != 0) {
ToastUtils.showToastInUiThread(MainApp.instance(), "aborting: previously applied constraint changed");
sendError("aborting: previously applied constraint changed");
return;
}
doFillBolus(amount);
} else if ("temptarget".equals(act[0])) {
int duration = SafeParse.stringToInt(act[2]);
double low = SafeParse.stringToDouble(act[3]);
double high = SafeParse.stringToDouble(act[4]);
boolean isMGDL = Boolean.parseBoolean(act[1]);
if (!isMGDL) {
low *= Constants.MMOLL_TO_MGDL;
high *= Constants.MMOLL_TO_MGDL;
}
generateTempTarget(duration, low, high);
} else if ("wizard2".equals(act[0])) {
if (lastBolusWizard != null) {
//use last calculation as confirmed string matches
doBolus(lastBolusWizard.getCalculatedTotalInsulin(), lastBolusWizard.getCarbs());
lastBolusWizard = null;
}
} else if ("bolus".equals(act[0])) {
double insulin = SafeParse.stringToDouble(act[1]);
int carbs = SafeParse.stringToInt(act[2]);
doBolus(insulin, carbs);
} else if ("cppset".equals(act[0])) {
int timeshift = SafeParse.stringToInt(act[1]);
int percentage = SafeParse.stringToInt(act[2]);
setCPP(timeshift, percentage);
} else if ("ecarbs".equals(act[0])) {
int carbs = SafeParse.stringToInt(act[1]);
long starttime = SafeParse.stringToLong(act[2]);
int duration = SafeParse.stringToInt(act[3]);
doECarbs(carbs, starttime, duration);
} else if ("dismissoverviewnotification".equals(act[0])) {
RxBus.Companion.getINSTANCE().send(new EventDismissNotification(SafeParse.stringToInt(act[1])));
} else if ("changeRequest".equals(act[0])) {
LoopPlugin.getPlugin().acceptChangeRequest();
NotificationManager notificationManager =
(NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(Constants.notificationID);
}
lastBolusWizard = null;
}
private static void doECarbs(int carbs, long time, int duration) {
if (carbs > 0) {
if (duration == 0) {
CarbsGenerator.createCarb(carbs, time, CareportalEvent.CARBCORRECTION, "watch");
} else {
CarbsGenerator.generateCarbs(carbs, time, duration, "watch eCarbs");
}
}
}
private static void setCPP(int timeshift, int percentage) {
String msg = "";
//check for validity
if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) {
msg += String.format(MainApp.gs(R.string.valueoutofrange), "Profile-Percentage") + "\n";
}
if (timeshift < 0 || timeshift > 23) {
msg += String.format(MainApp.gs(R.string.valueoutofrange), "Profile-Timeshift") + "\n";
}
final Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null) {
msg += MainApp.gs(R.string.notloadedplugins) + "\n";
}
if (!"".equals(msg)) {
msg += MainApp.gs(R.string.valuesnotstored);
String rTitle = "STATUS";
String rAction = "statusmessage";
WearPlugin.getPlugin().requestActionConfirmation(rTitle, msg, rAction);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = rAction;
return;
}
//send profile to pumpe
TreatmentsPlugin.getPlugin().doProfileSwitch(0, percentage, timeshift);
}
private static void generateTempTarget(int duration, double low, double high) {
TempTarget tempTarget = new TempTarget()
.date(System.currentTimeMillis())
.duration(duration)
.reason("WearPlugin")
.source(Source.USER);
if (tempTarget.durationInMinutes != 0) {
tempTarget.low(low).high(high);
} else {
tempTarget.low(0).high(0);
}
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
}
private static void doFillBolus(final Double amount) {
DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
detailedBolusInfo.insulin = amount;
detailedBolusInfo.isValid = false;
detailedBolusInfo.source = Source.USER;
ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() {
@Override
public void run() {
if (!result.success) {
sendError(MainApp.gs(R.string.treatmentdeliveryerror) +
"\n" +
result.comment);
}
}
});
}
private static void doBolus(final Double amount, final Integer carbs) {
DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
detailedBolusInfo.insulin = amount;
detailedBolusInfo.carbs = carbs;
detailedBolusInfo.source = Source.USER;
if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().storesCarbInfo) {
ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() {
@Override
public void run() {
if (!result.success) {
sendError(MainApp.gs(R.string.treatmentdeliveryerror) +
"\n" +
result.comment);
}
}
});
} else {
TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false);
}
}
private synchronized static void sendError(String errormessage) {
WearPlugin.getPlugin().requestActionConfirmation("ERROR", errormessage, "error");
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = null;
lastBolusWizard = null;
}
private synchronized static void sendStatusmessage(String title, String message) {
WearPlugin.getPlugin().requestActionConfirmation(title, message, "statusmessage");
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = null;
lastBolusWizard = null;
}
public synchronized static void expectNotificationAction(String message, int id) {
String actionstring = "dismissoverviewnotification " + id;
WearPlugin.getPlugin().requestActionConfirmation("DISMISS", message, actionstring);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = actionstring;
lastBolusWizard = null;
}
}

View file

@ -0,0 +1,619 @@
package info.nightscout.androidaps.plugins.general.wear
import android.app.NotificationManager
import android.content.Context
import dagger.Lazy
import info.nightscout.androidaps.Config
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.CareportalEvent
import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TDD
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ActionStringHandler @Inject constructor(
private val sp: SP,
private val rxBus: RxBusWrapper,
private val resourceHelper: ResourceHelper,
private val constraintChecker: ConstraintChecker,
private val profileFunction: ProfileFunction,
private val mainApp: MainApp,
private val loopPlugin: Lazy<LoopPlugin>,
private val wearPlugin: WearPlugin,
private val treatmentsPlugin: TreatmentsPlugin,
private val configBuilderPlugin: ConfigBuilderPlugin,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val danaRPlugin: DanaRPlugin,
private val danaRKoreanPlugin: DanaRKoreanPlugin,
private val danaRv2Plugin: DanaRv2Plugin,
private val danaRSPlugin: DanaRSPlugin
) {
private val TIMEOUT = 65 * 1000
private var lastSentTimestamp: Long = 0
private var lastConfirmActionString: String? = null
private var lastBolusWizard: BolusWizard? = null
@Synchronized
fun handleInitiate(actionString: String) {
if (!sp.getBoolean("wearcontrol", false)) return
lastBolusWizard = null
var rTitle = "CONFIRM" //TODO: i18n
var rMessage = ""
var rAction = ""
// do the parsing and check constraints
val act = actionString.split("\\s+").toTypedArray()
if ("fillpreset" == act[0]) { ///////////////////////////////////// PRIME/FILL
val amount: Double = if ("1" == act[1]) {
sp.getDouble("fill_button1", 0.3)
} else if ("2" == act[1]) {
sp.getDouble("fill_button2", 0.0)
} else if ("3" == act[1]) {
sp.getDouble("fill_button3", 0.0)
} else {
return
}
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
rMessage += resourceHelper.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + resourceHelper.gs(R.string.constraintapllied)
rAction += "fill $insulinAfterConstraints"
} else if ("fill" == act[0]) { ////////////////////////////////////////////// PRIME/FILL
val amount = SafeParse.stringToDouble(act[1])
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
rMessage += resourceHelper.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + resourceHelper.gs(R.string.constraintapllied)
rAction += "fill $insulinAfterConstraints"
} else if ("bolus" == act[0]) { ////////////////////////////////////////////// BOLUS
val insulin = SafeParse.stringToDouble(act[1])
val carbs = SafeParse.stringToInt(act[2])
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
rMessage += resourceHelper.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n"
rMessage += resourceHelper.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g"
if (insulinAfterConstraints - insulin != 0.0 || carbsAfterConstraints - carbs != 0) {
rMessage += "\n" + resourceHelper.gs(R.string.constraintapllied)
}
rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints"
} else if ("temptarget" == act[0]) { ///////////////////////////////////////////////////////// TEMPTARGET
val isMGDL = java.lang.Boolean.parseBoolean(act[1])
if (profileFunction.getUnits() == Constants.MGDL != isMGDL) {
sendError("Different units used on watch and phone!")
return
}
val duration = SafeParse.stringToInt(act[2])
if (duration == 0) {
rMessage += "Zero-Temp-Target - cancelling running Temp-Targets?"
rAction = "temptarget true 0 0 0"
} else {
var low = SafeParse.stringToDouble(act[3])
var high = SafeParse.stringToDouble(act[4])
if (!isMGDL) {
low *= Constants.MMOLL_TO_MGDL
high *= Constants.MMOLL_TO_MGDL
}
if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) {
sendError("Min-BG out of range!")
return
}
if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) {
sendError("Max-BG out of range!")
return
}
rMessage += "Temptarget:\nMin: " + act[3] + "\nMax: " + act[4] + "\nDuration: " + act[2]
rAction = actionString
}
} else if ("status" == act[0]) { ////////////////////////////////////////////// STATUS
rTitle = "STATUS"
rAction = "statusmessage"
if ("pump" == act[1]) {
rTitle += " PUMP"
rMessage = pumpStatus
} else if ("loop" == act[1]) {
rTitle += " LOOP"
rMessage = "TARGETS:\n$targetsStatus"
rMessage += "\n\n" + loopStatus
rMessage += "\n\nOAPS RESULT:\n$oAPSResultStatus"
}
} else if ("wizard" == act[0]) {
sendError("Update APP on Watch!")
return
} else if ("wizard2" == act[0]) { ////////////////////////////////////////////// WIZARD
val carbsBeforeConstraints = SafeParse.stringToInt(act[1])
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value()
if (carbsAfterConstraints - carbsBeforeConstraints != 0) {
sendError("Carb constraint violation!")
return
}
val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true)
val useTT = sp.getBoolean(R.string.key_wearwizard_tt, false)
val useBolusIOB = sp.getBoolean(R.string.key_wearwizard_bolusiob, true)
val useBasalIOB = sp.getBoolean(R.string.key_wearwizard_basaliob, true)
val useCOB = sp.getBoolean(R.string.key_wearwizard_cob, true)
val useTrend = sp.getBoolean(R.string.key_wearwizard_trend, false)
val percentage = act[2].toInt()
val profile = profileFunction.getProfile()
val profileName = profileFunction.getProfileName()
if (profile == null) {
sendError("No profile found!")
return
}
val bgReading = DatabaseHelper.actualBg()
if (bgReading == null && useBG) {
sendError("No recent BG to base calculation on!")
return
}
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard wear")
if (useCOB && (cobInfo.displayCob == null)) {
sendError("Unknown COB! BG reading missing or recent app restart?")
return
}
val format = DecimalFormat("0.00")
val formatInt = DecimalFormat("0")
val bolusWizard = BolusWizard(profile, profileName, treatmentsPlugin.tempTargetFromHistory,
carbsAfterConstraints, cobInfo.displayCob!!, bgReading!!.valueToUnits(profileFunction.getUnits()),
0.0, percentage.toDouble(), useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend)
if (Math.abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) {
sendError("Insulin constraint violation!" +
"\nCannot deliver " + format.format(bolusWizard.calculatedTotalInsulin) + "!")
return
}
if (bolusWizard.calculatedTotalInsulin <= 0 && bolusWizard.carbs <= 0) {
rAction = "info"
rTitle = "INFO"
} else {
rAction = actionString
}
rMessage += "Carbs: " + bolusWizard.carbs + "g"
rMessage += "\nBolus: " + format.format(bolusWizard.calculatedTotalInsulin) + "U"
rMessage += "\n_____________"
rMessage += "\nCalc (IC:" + DecimalFormatter.to1Decimal(bolusWizard.ic) + ", " + "ISF:" + DecimalFormatter.to1Decimal(bolusWizard.sens) + "): "
rMessage += "\nFrom Carbs: " + format.format(bolusWizard.insulinFromCarbs) + "U"
if (useCOB) rMessage += "\nFrom" + formatInt.format(cobInfo.displayCob) + "g COB : " + format.format(bolusWizard.insulinFromCOB) + "U"
if (useBG) rMessage += "\nFrom BG: " + format.format(bolusWizard.insulinFromBG) + "U"
if (useBolusIOB) rMessage += "\nBolus IOB: " + format.format(bolusWizard.insulinFromBolusIOB) + "U"
if (useBasalIOB) rMessage += "\nBasal IOB: " + format.format(bolusWizard.insulinFromBasalsIOB) + "U"
if (useTrend) rMessage += "\nFrom 15' trend: " + format.format(bolusWizard.insulinFromTrend) + "U"
if (percentage != 100) {
rMessage += "\nPercentage: " + format.format(bolusWizard.totalBeforePercentageAdjustment) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.calculatedTotalInsulin) + "U"
}
lastBolusWizard = bolusWizard
} else if ("opencpp" == act[0]) {
val activeProfileSwitch = treatmentsPlugin.getProfileSwitchFromHistory(System.currentTimeMillis())
if (activeProfileSwitch == null) {
sendError("No active profile switch!")
return
} else { // read CPP values
rTitle = "opencpp"
rMessage = "opencpp"
rAction = "opencpp" + " " + activeProfileSwitch.percentage + " " + activeProfileSwitch.timeshift
}
} else if ("cppset" == act[0]) {
val activeProfileSwitch = treatmentsPlugin.getProfileSwitchFromHistory(System.currentTimeMillis())
if (activeProfileSwitch == null) {
sendError("No active profile switch!")
return
} else { // read CPP values
rMessage = "CPP:" + "\n\n" +
"Timeshift: " + act[1] + "\n" +
"Percentage: " + act[2] + "%"
rAction = actionString
}
} else if ("tddstats" == act[0]) {
val activePump: Any? = configBuilderPlugin.activePump
if (activePump != null) { // check if DB up to date
val dummies: MutableList<TDD> = LinkedList()
val historyList = getTDDList(dummies)
if (isOldData(historyList)) {
rTitle = "TDD"
rAction = "statusmessage"
rMessage = "OLD DATA - "
//if pump is not busy: try to fetch data
val pump = configBuilderPlugin.activePump
if (pump!!.isBusy) {
rMessage += resourceHelper.gs(R.string.pumpbusy)
} else {
rMessage += "trying to fetch data from pump."
configBuilderPlugin.commandQueue.loadTDDs(object : Callback() {
override fun run() {
val dummies1: MutableList<TDD> = LinkedList()
val historyList1 = getTDDList(dummies1)
if (isOldData(historyList1)) {
sendStatusMessage("TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1))
} else {
sendStatusMessage(generateTDDMessage(historyList1, dummies1))
}
}
})
}
} else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats)
rTitle = "TDD"
rAction = "statusmessage"
rMessage = generateTDDMessage(historyList, dummies)
}
}
} else if ("ecarbs" == act[0]) { ////////////////////////////////////////////// ECARBS
val carbs = SafeParse.stringToInt(act[1])
val starttime = SafeParse.stringToInt(act[2])
val duration = SafeParse.stringToInt(act[3])
val starttimestamp = System.currentTimeMillis() + starttime * 60 * 1000
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
rMessage += resourceHelper.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g"
rMessage += "\n" + resourceHelper.gs(R.string.time) + ": " + DateUtil.timeString(starttimestamp)
rMessage += "\n" + resourceHelper.gs(R.string.duration) + ": " + duration + "h"
if (carbsAfterConstraints - carbs != 0) {
rMessage += "\n" + resourceHelper.gs(R.string.constraintapllied)
}
if (carbsAfterConstraints <= 0) {
sendError("Carbs = 0! No action taken!")
return
}
rAction += "ecarbs $carbsAfterConstraints $starttimestamp $duration"
} else if ("changeRequest" == act[0]) { ////////////////////////////////////////////// CHANGE REQUEST
rTitle = resourceHelper.gs(R.string.openloop_newsuggestion)
rAction = "changeRequest"
val finalLastRun = LoopPlugin.lastRun
rMessage += finalLastRun.constraintsProcessed
wearPlugin.requestChangeConfirmation(rTitle, rMessage, rAction)
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = rAction
return
} else if ("cancelChangeRequest" == act[0]) { ////////////////////////////////////////////// CANCEL CHANGE REQUEST NOTIFICATION
rAction = "cancelChangeRequest"
wearPlugin.requestNotificationCancel(rAction)
return
} else return
// send result
wearPlugin.requestActionConfirmation(rTitle, rMessage, rAction)
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = rAction
}
private fun generateTDDMessage(historyList: MutableList<TDD>, dummies: MutableList<TDD>): String {
val profile = profileFunction.getProfile() ?: return "No profile loaded :("
if (historyList.isEmpty()) {
return "No history data!"
}
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
var message = ""
val refTDD = profile.baseBasalSum() * 2
val pump = configBuilderPlugin.activePump
if (df.format(Date(historyList[0].date)) == df.format(Date())) {
val tdd = historyList[0].getTotal()
historyList.removeAt(0)
message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n"
message += "\n"
} else if (pump != null && pump is DanaRPlugin) {
val tdd = DanaRPump.getInstance().dailyTotalUnits
message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n"
message += "\n"
}
var i = 0
var weighted03 = 0.0
var weighted05 = 0.0
var weighted07 = 0.0
Collections.reverse(historyList)
for (record in historyList) {
val tdd = record.getTotal()
if (i == 0) {
weighted03 = tdd
weighted05 = tdd
weighted07 = tdd
} else {
weighted07 = weighted07 * 0.3 + tdd * 0.7
weighted05 = weighted05 * 0.5 + tdd * 0.5
weighted03 = weighted03 * 0.7 + tdd * 0.3
}
i++
}
message += "weighted:\n"
message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n"
message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n"
message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n"
message += "\n"
Collections.reverse(historyList)
//add TDDs:
for (record in historyList) {
val tdd = record.getTotal()
message += df.format(Date(record.date)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(record)) "x" else "") + "\n"
}
return message
}
private fun isOldData(historyList: List<TDD>): Boolean {
val activePump: Any? = configBuilderPlugin.activePump
val insight: PumpInterface = LocalInsightPlugin.getPlugin()
val startsYesterday = activePump === danaRPlugin || activePump === danaRSPlugin || activePump === danaRv2Plugin || activePump === danaRKoreanPlugin || activePump === insight
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
return historyList.size < 3 || df.format(Date(historyList[0].date)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0))
}
private fun getTDDList(returnDummies: MutableList<TDD>): MutableList<TDD> {
var historyList = MainApp.getDbHelper().tdDs
historyList = historyList.subList(0, Math.min(10, historyList.size))
//fill single gaps - only needed for Dana*R data
val dummies: MutableList<TDD> = returnDummies
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
for (i in 0 until historyList.size - 1) {
val elem1 = historyList[i]
val elem2 = historyList[i + 1]
if (df.format(Date(elem1!!.date)) != df.format(Date(elem2!!.date + 25 * 60 * 60 * 1000))) {
val dummy = TDD()
dummy.date = elem1.date - 24 * 60 * 60 * 1000
dummy.basal = elem1.basal / 2
dummy.bolus = elem1.bolus / 2
dummies.add(dummy)
elem1.basal /= 2.0
elem1.bolus /= 2.0
}
}
historyList.addAll(dummies)
Collections.sort(historyList) { lhs, rhs -> (rhs.date - lhs.date).toInt() }
return historyList
}
private val pumpStatus: String
get() = configBuilderPlugin.activePump?.shortStatus(false) ?: ""
// decide if enabled/disabled closed/open; what Plugin as APS?
private val loopStatus: String
get() {
var ret = ""
// decide if enabled/disabled closed/open; what Plugin as APS?
if (loopPlugin.get().isEnabled(loopPlugin.get().getType())) {
ret += if (constraintChecker.isClosedLoopAllowed().value()) {
"CLOSED LOOP\n"
} else {
"OPEN LOOP\n"
}
val aps = configBuilderPlugin.activeAPS
ret += "APS: " + if (aps == null) "NO APS SELECTED!" else (aps as PluginBase).name
if (LoopPlugin.lastRun != null) {
if (LoopPlugin.lastRun.lastAPSRun != null) ret += "\nLast Run: " + DateUtil.timeString(LoopPlugin.lastRun.lastAPSRun)
if (LoopPlugin.lastRun.lastEnact != null) ret += "\nLast Enact: " + DateUtil.timeString(LoopPlugin.lastRun.lastEnact)
}
} else {
ret += "LOOP DISABLED\n"
}
return ret
}
//Check for Temp-Target:
private val targetsStatus: String
get() {
var ret = ""
if (!Config.APS) {
return "Targets only apply in APS mode!"
}
val profile = profileFunction.getProfile() ?: return "No profile set :("
//Check for Temp-Target:
val tempTarget = treatmentsPlugin.tempTargetFromHistory
if (tempTarget != null) {
ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.low, tempTarget.low, Constants.MGDL, profileFunction.getUnits())
ret += "\nuntil: " + DateUtil.timeString(tempTarget.originalEnd())
ret += "\n\n"
}
ret += "DEFAULT RANGE: "
ret += Profile.fromMgdlToUnits(profile.targetLowMgdl, profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(profile.targetHighMgdl, profileFunction.getUnits())
ret += " target: " + Profile.fromMgdlToUnits(profile.targetMgdl, profileFunction.getUnits())
return ret
}
private val oAPSResultStatus: String
get() {
var ret = ""
if (!Config.APS) {
return "Only apply in APS mode!"
}
val usedAPS = configBuilderPlugin.activeAPS ?: return "No active APS :(!"
val result = usedAPS.lastAPSResult ?: return "Last result not available!"
ret += if (!result.isChangeRequested) {
resourceHelper.gs(R.string.nochangerequested) + "\n"
} else if (result.rate == 0.0 && result.duration == 0) {
resourceHelper.gs(R.string.canceltemp) + "\n"
} else {
resourceHelper.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " +
"(" + DecimalFormatter.to2Decimal(result.rate / configBuilderPlugin.activePump!!.baseBasalRate * 100) + "%)\n" +
resourceHelper.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration.toDouble()) + " min\n"
}
ret += "\n" + resourceHelper.gs(R.string.reason) + ": " + result.reason
return ret
}
@Synchronized
fun handleConfirmation(actionString: String) {
if (!sp.getBoolean("wearcontrol", false)) return
//Guard from old or duplicate confirmations
if (lastConfirmActionString == null) return
if (lastConfirmActionString != actionString) return
if (System.currentTimeMillis() - lastSentTimestamp > TIMEOUT) return
lastConfirmActionString = null
// do the parsing, check constraints and enact!
val act = actionString.split("\\s+").toTypedArray()
if ("fill" == act[0]) {
val amount = SafeParse.stringToDouble(act[1])
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
if (amount - insulinAfterConstraints != 0.0) {
ToastUtils.showToastInUiThread(mainApp, "aborting: previously applied constraint changed")
sendError("aborting: previously applied constraint changed")
return
}
doFillBolus(amount)
} else if ("temptarget" == act[0]) {
val duration = SafeParse.stringToInt(act[2])
var low = SafeParse.stringToDouble(act[3])
var high = SafeParse.stringToDouble(act[4])
val isMGDL = java.lang.Boolean.parseBoolean(act[1])
if (!isMGDL) {
low *= Constants.MMOLL_TO_MGDL
high *= Constants.MMOLL_TO_MGDL
}
generateTempTarget(duration, low, high)
} else if ("wizard2" == act[0]) {
if (lastBolusWizard != null) { //use last calculation as confirmed string matches
doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs)
lastBolusWizard = null
}
} else if ("bolus" == act[0]) {
val insulin = SafeParse.stringToDouble(act[1])
val carbs = SafeParse.stringToInt(act[2])
doBolus(insulin, carbs)
} else if ("cppset" == act[0]) {
val timeshift = SafeParse.stringToInt(act[1])
val percentage = SafeParse.stringToInt(act[2])
setCPP(timeshift, percentage)
} else if ("ecarbs" == act[0]) {
val carbs = SafeParse.stringToInt(act[1])
val starttime = SafeParse.stringToLong(act[2])
val duration = SafeParse.stringToInt(act[3])
doECarbs(carbs, starttime, duration)
} else if ("dismissoverviewnotification" == act[0]) {
rxBus.send(EventDismissNotification(SafeParse.stringToInt(act[1])))
} else if ("changeRequest" == act[0]) {
loopPlugin.get().acceptChangeRequest()
val notificationManager = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(Constants.notificationID)
}
lastBolusWizard = null
}
private fun doECarbs(carbs: Int, time: Long, duration: Int) {
if (carbs > 0) {
if (duration == 0) {
CarbsGenerator.createCarb(carbs, time, CareportalEvent.CARBCORRECTION, "watch")
} else {
CarbsGenerator.generateCarbs(carbs, time, duration, "watch eCarbs")
}
}
}
private fun setCPP(timeshift: Int, percentage: Int) {
var msg = ""
//check for validity
if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) {
msg += String.format(resourceHelper.gs(R.string.valueoutofrange), "Profile-Percentage") + "\n"
}
if (timeshift < 0 || timeshift > 23) {
msg += String.format(resourceHelper.gs(R.string.valueoutofrange), "Profile-Timeshift") + "\n"
}
val profile = profileFunction.getProfile()
if (profile == null) {
msg += resourceHelper.gs(R.string.notloadedplugins) + "\n"
}
if ("" != msg) {
msg += resourceHelper.gs(R.string.valuesnotstored)
val rTitle = "STATUS"
val rAction = "statusmessage"
wearPlugin.requestActionConfirmation(rTitle, msg, rAction)
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = rAction
return
}
//send profile to pumpe
treatmentsPlugin.doProfileSwitch(0, percentage, timeshift)
}
private fun generateTempTarget(duration: Int, low: Double, high: Double) {
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(duration)
.reason("WearPlugin")
.source(Source.USER)
if (tempTarget.durationInMinutes != 0) {
tempTarget.low(low).high(high)
} else {
tempTarget.low(0.0).high(0.0)
}
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
}
private fun doFillBolus(amount: Double) {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = amount
detailedBolusInfo.isValid = false
detailedBolusInfo.source = Source.USER
configBuilderPlugin.commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
sendError(resourceHelper.gs(R.string.treatmentdeliveryerror) +
"\n" +
result.comment)
}
}
})
}
private fun doBolus(amount: Double, carbs: Int) {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = amount
detailedBolusInfo.carbs = carbs.toDouble()
detailedBolusInfo.source = Source.USER
if (detailedBolusInfo.insulin > 0 || configBuilderPlugin.activePump!!.pumpDescription.storesCarbInfo) {
configBuilderPlugin.commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
sendError(resourceHelper.gs(R.string.treatmentdeliveryerror) +
"\n" +
result.comment)
}
}
})
} else {
treatmentsPlugin.addToHistoryTreatment(detailedBolusInfo, false)
}
}
@Synchronized private fun sendError(errormessage: String) {
wearPlugin.requestActionConfirmation("ERROR", errormessage, "error")
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = null
lastBolusWizard = null
}
@Synchronized
private fun sendStatusMessage(message: String) {
wearPlugin.requestActionConfirmation("TDD", message, "statusmessage")
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = null
lastBolusWizard = null
} /*
public synchronized static void expectNotificationAction(String message, int id) {
String actionstring = "dismissoverviewnotification " + id;
WearPlugin.getPlugin().requestActionConfirmation("DISMISS", message, actionstring);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = actionstring;
lastBolusWizard = null;
}
*/
}

View file

@ -1,39 +0,0 @@
package info.nightscout.androidaps.plugins.general.wear;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import info.nightscout.androidaps.R;
/**
* Created by adrian on 17/11/16.
*/
public class WearFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.wear_fragment, container, false);
view.findViewById(R.id.wear_resend).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
WearPlugin.getPlugin().resendDataToWatch();
}
});
view.findViewById(R.id.wear_opensettings).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
WearPlugin.getPlugin().openSettings();
}
});
return view;
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.general.wear
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import kotlinx.android.synthetic.main.wear_fragment.*
import javax.inject.Inject
class WearFragment : DaggerFragment() {
@Inject lateinit var wearPlugin: WearPlugin
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.wear_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
wear_resend.setOnClickListener { wearPlugin.resendDataToWatch() }
wear_opensettings.setOnClickListener { wearPlugin.openSettings() }
}
}

View file

@ -1,244 +0,0 @@
package info.nightscout.androidaps.plugins.general.wear;
import android.content.Context;
import android.content.Intent;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventBolusRequested;
import info.nightscout.androidaps.events.EventExtendedBolusChange;
import info.nightscout.androidaps.events.EventNewBasalProfile;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.events.EventTempBasalChange;
import info.nightscout.androidaps.events.EventTreatmentChange;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning;
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.SP;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
/**
* Created by adrian on 17/11/16.
*/
public class WearPlugin extends PluginBase {
private static WatchUpdaterService watchUS;
private final Context ctx;
private static WearPlugin wearPlugin;
private static String TAG = "WearPlugin";
private CompositeDisposable disposable = new CompositeDisposable();
@Deprecated
public static WearPlugin getPlugin() {
return wearPlugin;
}
public static WearPlugin initPlugin(Context ctx) {
if (wearPlugin == null) {
wearPlugin = new WearPlugin(ctx);
}
return wearPlugin;
}
WearPlugin(Context ctx) {
super(new PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(WearFragment.class.getName())
.pluginName(R.string.wear)
.shortName(R.string.wear_shortname)
.preferencesId(R.xml.pref_wear)
.description(R.string.description_wear)
);
this.ctx = ctx;
}
@Override
protected void onStart() {
if (watchUS != null) {
watchUS.setSettings();
}
super.onStart();
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventOpenAPSUpdateGui.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(true, true, false),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventExtendedBolusChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(true, true, false),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventTempBasalChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(true, true, false),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventTreatmentChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(true, true, false),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventNewBasalProfile.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(false, true, false),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventAutosensCalculationFinished.class)
.observeOn(Schedulers.io())
.subscribe(event -> sendDataToWatch(true, true, true),
FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventPreferenceChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
// possibly new high or low mark
resendDataToWatch();
// status may be formated differently
sendDataToWatch(true, false, false);
}, FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventRefreshOverview.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
if (WatchUpdaterService.shouldReportLoopStatus(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)))
sendDataToWatch(true, false, false);
}, FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventBolusRequested.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
String status = String.format(MainApp.gs(R.string.bolusrequested), event.getAmount());
Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS);
intent.putExtra("progresspercent", 0);
intent.putExtra("progressstatus", status);
ctx.startService(intent);
}, FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventDismissBolusProgressIfRunning.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
if (event.getResult() == null) return;
String status;
if (event.getResult().success) {
status = MainApp.gs(R.string.success);
} else {
status = MainApp.gs(R.string.nosuccess);
}
Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS);
intent.putExtra("progresspercent", 100);
intent.putExtra("progressstatus", status);
ctx.startService(intent);
}, FabricPrivacy::logException
));
disposable.add(RxBus.Companion.getINSTANCE()
.toObservable(EventOverviewBolusProgress.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
if (!event.isSMB() || SP.getBoolean("wear_notifySMB", true)) {
Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS);
intent.putExtra("progresspercent", event.getPercent());
intent.putExtra("progressstatus", event.getStatus());
ctx.startService(intent);
}
}, FabricPrivacy::logException
));
}
@Override
protected void onStop() {
disposable.clear();
super.onStop();
}
private void sendDataToWatch(boolean status, boolean basals, boolean bgValue) {
//Log.d(TAG, "WR: WearPlugin:sendDataToWatch (status=" + status + ",basals=" + basals + ",bgValue=" + bgValue + ")");
if (isEnabled(getType())) { // only start service when this plugin is enabled
if (bgValue) {
ctx.startService(new Intent(ctx, WatchUpdaterService.class));
}
if (basals) {
ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BASALS));
}
if (status) {
ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_STATUS));
}
}
}
void resendDataToWatch() {
//Log.d(TAG, "WR: WearPlugin:resendDataToWatch");
ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_RESEND));
}
void openSettings() {
//Log.d(TAG, "WR: WearPlugin:openSettings");
ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS));
}
void requestNotificationCancel(String actionstring) {
//Log.d(TAG, "WR: WearPlugin:requestNotificationCancel");
Intent intent = new Intent(ctx, WatchUpdaterService.class)
.setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION);
intent.putExtra("actionstring", actionstring);
ctx.startService(intent);
}
public void requestActionConfirmation(String title, String message, String actionstring) {
Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST);
intent.putExtra("title", title);
intent.putExtra("message", message);
intent.putExtra("actionstring", actionstring);
ctx.startService(intent);
}
public void requestChangeConfirmation(String title, String message, String actionstring) {
Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST);
intent.putExtra("title", title);
intent.putExtra("message", message);
intent.putExtra("actionstring", actionstring);
ctx.startService(intent);
}
public static void registerWatchUpdaterService(WatchUpdaterService wus) {
watchUS = wus;
}
public static void unRegisterWatchUpdaterService() {
watchUS = null;
}
}

View file

@ -0,0 +1,173 @@
package info.nightscout.androidaps.plugins.general.wear
import android.content.Intent
import dagger.Lazy
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress
import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class WearPlugin internal @Inject constructor(
private val rxBus: RxBusWrapper,
private val resourceHelper: ResourceHelper,
private val sp: SP,
private val mainApp: MainApp,
private val loopPlugin: Lazy<LoopPlugin>
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(WearFragment::class.java.name)
.pluginName(R.string.wear)
.shortName(R.string.wear_shortname)
.preferencesId(R.xml.pref_wear)
.description(R.string.description_wear)
) {
private val disposable = CompositeDisposable()
override fun onStart() {
super.onStart()
disposable.add(rxBus
.toObservable(EventOpenAPSUpdateGui::class.java)
.observeOn(Schedulers.io())
.subscribe({ sendDataToWatch(true, true, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ sendDataToWatch(true, true, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventTempBasalChange? -> sendDataToWatch(true, true, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ sendDataToWatch(true, true, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventNewBasalProfile::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventNewBasalProfile? -> sendDataToWatch(false, true, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventAutosensCalculationFinished? -> sendDataToWatch(true, true, true) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventPreferenceChange? ->
// possibly new high or low mark
resendDataToWatch()
// status may be formated differently
sendDataToWatch(true, false, false)
}) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventRefreshOverview::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventRefreshOverview? -> if (WatchUpdaterService.shouldReportLoopStatus(loopPlugin.get().isEnabled(PluginType.LOOP))) sendDataToWatch(true, false, false) }) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventBolusRequested::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventBolusRequested ->
val status = String.format(resourceHelper.gs(R.string.bolusrequested), event.amount)
val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS)
intent.putExtra("progresspercent", 0)
intent.putExtra("progressstatus", status)
mainApp.startService(intent)
}) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventDismissBolusProgressIfRunning::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventDismissBolusProgressIfRunning ->
if (event.result == null) return@subscribe
val status: String = if (event.result.success) {
resourceHelper.gs(R.string.success)
} else {
resourceHelper.gs(R.string.nosuccess)
}
val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS)
intent.putExtra("progresspercent", 100)
intent.putExtra("progressstatus", status)
mainApp.startService(intent)
}) { FabricPrivacy.logException(it) })
disposable.add(rxBus
.toObservable(EventOverviewBolusProgress::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventOverviewBolusProgress ->
if (!event.isSMB() || sp.getBoolean("wear_notifySMB", true)) {
val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS)
intent.putExtra("progresspercent", event.percent)
intent.putExtra("progressstatus", event.status)
mainApp.startService(intent)
}
}) { FabricPrivacy.logException(it) })
}
override fun onStop() {
disposable.clear()
super.onStop()
}
private fun sendDataToWatch(status: Boolean, basals: Boolean, bgValue: Boolean) {
//Log.d(TAG, "WR: WearPlugin:sendDataToWatch (status=" + status + ",basals=" + basals + ",bgValue=" + bgValue + ")");
if (isEnabled(getType())) {
// only start service when this plugin is enabled
if (bgValue) {
mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java))
}
if (basals) {
mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BASALS))
}
if (status) {
mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_STATUS))
}
}
}
fun resendDataToWatch() {
//Log.d(TAG, "WR: WearPlugin:resendDataToWatch");
mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND))
}
fun openSettings() {
//Log.d(TAG, "WR: WearPlugin:openSettings");
mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS))
}
fun requestNotificationCancel(actionstring: String?) { //Log.d(TAG, "WR: WearPlugin:requestNotificationCancel");
val intent = Intent(mainApp, WatchUpdaterService::class.java)
.setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION)
intent.putExtra("actionstring", actionstring)
mainApp.startService(intent)
}
fun requestActionConfirmation(title: String, message: String, actionString: String) {
val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST)
intent.putExtra("title", title)
intent.putExtra("message", message)
intent.putExtra("actionstring", actionString)
mainApp.startService(intent)
}
fun requestChangeConfirmation(title: String, message: String, actionString: String) {
val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST)
intent.putExtra("title", title)
intent.putExtra("message", message)
intent.putExtra("actionstring", actionString)
mainApp.startService(intent)
}
}

View file

@ -3,13 +3,11 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -26,13 +24,13 @@ import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService; import com.google.android.gms.wearable.WearableListenerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
@ -43,9 +41,11 @@ import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin;
@ -56,10 +56,21 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorP
import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.Treatment;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.ToastUtils; import info.nightscout.androidaps.utils.ToastUtils;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
@Inject public AAPSLogger aapsLogger;
@Inject public WearPlugin wearPlugin;
@Inject public ResourceHelper resourceHelper;
@Inject public SP sp;
@Inject public ConfigBuilderPlugin configBuilderPlugin;
@Inject public ProfileFunction profileFunction;
@Inject public LoopPlugin loopPlugin;
@Inject public IobCobCalculatorPlugin iobCobCalculatorPlugin;
@Inject public TreatmentsPlugin treatmentsPlugin;
@Inject public ActionStringHandler actionStringHandler;
public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend"); public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend");
public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings"); public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings");
@ -87,11 +98,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest";
boolean wear_integration = false;
private static boolean lastLoopStatus; private static boolean lastLoopStatus;
private static Logger log = LoggerFactory.getLogger(WatchUpdaterService.class);
private Handler handler; private Handler handler;
// Phone // Phone
@ -100,16 +108,12 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Wear // Wear
private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs"; private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs";
private static final String MESSAGE_PATH_WEAR = "/wear_message_path"; private static final String MESSAGE_PATH_WEAR = "/wear_message_path";
private String mWearNodeId = null;
private String localnode = null;
private String logPrefix = ""; // "WR: "
@Override @Override
public void onCreate() { public void onCreate() {
listenForChangeInSettings(); AndroidInjection.inject(this);
setSettings(); if (wearIntegration()) {
if (wear_integration) {
googleApiConnect(); googleApiConnect();
} }
if (handler == null) { if (handler == null) {
@ -119,19 +123,10 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
} }
} }
public void listenForChangeInSettings() { private boolean wearIntegration() {
WearPlugin.registerWatchUpdaterService(this); return wearPlugin.isEnabled(PluginType.GENERAL);
} }
public void setSettings() {
wear_integration = WearPlugin.getPlugin().isEnabled(PluginType.GENERAL);
// Log.d(TAG, "WR: wear_integration=" + wear_integration);
if (wear_integration) {
googleApiConnect();
}
}
private void googleApiConnect() { private void googleApiConnect() {
if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) { if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) {
googleApiClient.disconnect(); googleApiClient.disconnect();
@ -140,7 +135,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
.addOnConnectionFailedListener(this).addApi(Wearable.API).build(); .addOnConnectionFailedListener(this).addApi(Wearable.API).build();
Wearable.MessageApi.addListener(googleApiClient, this); Wearable.MessageApi.addListener(googleApiClient, this);
if (googleApiClient.isConnected()) { if (googleApiClient.isConnected()) {
log.debug(logPrefix + "API client is connected"); aapsLogger.debug(LTag.WEAR, "API client is connected");
} else { } else {
// Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect"); // Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect");
googleApiClient.connect(); googleApiClient.connect();
@ -153,7 +148,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Log.d(TAG, logPrefix + "onStartCommand: " + action); // Log.d(TAG, logPrefix + "onStartCommand: " + action);
if (wear_integration) { if (wearIntegration()) {
handler.post(() -> { handler.post(() -> {
if (googleApiClient.isConnected()) { if (googleApiClient.isConnected()) {
if (ACTION_RESEND.equals(action)) { if (ACTION_RESEND.equals(action)) {
@ -193,9 +188,9 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) { private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) {
Log.d("WatchUpdaterService", logPrefix + "CabilityInfo: " + capabilityInfo); Log.d("WatchUpdaterService", "CabilityInfo: " + capabilityInfo);
Set<Node> connectedNodes = capabilityInfo.getNodes(); Set<Node> connectedNodes = capabilityInfo.getNodes();
mWearNodeId = pickBestNodeId(connectedNodes); String mWearNodeId = pickBestNodeId(connectedNodes);
} }
@ -240,7 +235,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
String id = peer.getId(); String id = peer.getId();
String name = peer.getDisplayName(); String name = peer.getDisplayName();
// Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id); // Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
} }
@ -249,7 +243,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Log.d(TAG, logPrefix + "onMessageRecieved: " + event); // Log.d(TAG, logPrefix + "onMessageRecieved: " + event);
if (wear_integration) { if (wearIntegration()) {
if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) { if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) {
resendData(); resendData();
} }
@ -260,20 +254,20 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (event != null && event.getPath().equals(WEARABLE_INITIATE_ACTIONSTRING_PATH)) { if (event != null && event.getPath().equals(WEARABLE_INITIATE_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData()); String actionstring = new String(event.getData());
log.debug("Wear: " + actionstring); aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring);
ActionStringHandler.handleInitiate(actionstring); actionStringHandler.handleInitiate(actionstring);
} }
if (event != null && event.getPath().equals(WEARABLE_CONFIRM_ACTIONSTRING_PATH)) { if (event != null && event.getPath().equals(WEARABLE_CONFIRM_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData()); String actionstring = new String(event.getData());
log.debug("Wear Confirm: " + actionstring); aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring);
ActionStringHandler.handleConfirmation(actionstring); actionStringHandler.handleConfirmation(actionstring);
} }
} }
} }
private void cancelBolus() { private void cancelBolus() {
ConfigBuilderPlugin.getPlugin().getActivePump().stopBolusDelivering(); configBuilderPlugin.getActivePump().stopBolusDelivering();
} }
private void sendData() { private void sendData() {
@ -286,11 +280,11 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
googleApiConnect(); googleApiConnect();
} }
if (wear_integration) { if (wearIntegration()) {
final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus); final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus);
if (dataMap == null) { if (dataMap == null) {
ToastUtils.showToastInUiThread(this, MainApp.gs(R.string.noprofile)); ToastUtils.showToastInUiThread(this, resourceHelper.gs(R.string.noprofile));
return; return;
} }
@ -356,7 +350,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
deltastring += "-"; deltastring += "-";
} }
boolean detailed = SP.getBoolean("wear_detailed_delta", false); boolean detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false);
if (units.equals(Constants.MGDL)) { if (units.equals(Constants.MGDL)) {
if (detailed) { if (detailed) {
deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMGDL)); deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMGDL));
@ -407,7 +401,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (!graph_bgs.isEmpty()) { if (!graph_bgs.isEmpty()) {
DataMap entries = dataMapSingleBG(last_bg, glucoseStatus); DataMap entries = dataMapSingleBG(last_bg, glucoseStatus);
if (entries == null) { if (entries == null) {
ToastUtils.showToastInUiThread(this, MainApp.gs(R.string.noprofile)); ToastUtils.showToastInUiThread(this, resourceHelper.gs(R.string.noprofile));
return; return;
} }
final ArrayList<DataMap> dataMaps = new ArrayList<>(graph_bgs.size()); final ArrayList<DataMap> dataMaps = new ArrayList<>(graph_bgs.size());
@ -440,7 +434,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
ArrayList<DataMap> predictions = new ArrayList<>(); ArrayList<DataMap> predictions = new ArrayList<>();
Profile profile = ProfileFunctions.getInstance().getProfile(); Profile profile = profileFunction.getProfile();
if (profile == null) { if (profile == null) {
return; return;
@ -452,8 +446,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
double beginBasalValue = profile.getBasal(beginBasalSegmentTime); double beginBasalValue = profile.getBasal(beginBasalSegmentTime);
double endBasalValue = beginBasalValue; double endBasalValue = beginBasalValue;
TemporaryBasal tb1 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(runningTime); TemporaryBasal tb1 = treatmentsPlugin.getTempBasalFromHistory(runningTime);
TemporaryBasal tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(runningTime); TemporaryBasal tb2 = treatmentsPlugin.getTempBasalFromHistory(runningTime);
double tb_before = beginBasalValue; double tb_before = beginBasalValue;
double tb_amount = beginBasalValue; double tb_amount = beginBasalValue;
long tb_start = runningTime; long tb_start = runningTime;
@ -469,7 +463,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
for (; runningTime < now; runningTime += 5 * 60 * 1000) { for (; runningTime < now; runningTime += 5 * 60 * 1000) {
Profile profileTB = ProfileFunctions.getInstance().getProfile(runningTime); Profile profileTB = profileFunction.getProfile(runningTime);
if (profileTB == null) if (profileTB == null)
return; return;
//basal rate //basal rate
@ -484,7 +478,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
} }
//temps //temps
tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(runningTime); tb2 = treatmentsPlugin.getTempBasalFromHistory(runningTime);
if (tb1 == null && tb2 == null) { if (tb1 == null && tb2 == null) {
//no temp stays no temp //no temp stays no temp
@ -517,13 +511,13 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue));
} }
if (tb1 != null) { if (tb1 != null) {
tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); //use "now" to express current situation tb2 = treatmentsPlugin.getTempBasalFromHistory(now); //use "now" to express current situation
if (tb2 == null) { if (tb2 == null) {
//express the cancelled temp by painting it down one minute early //express the cancelled temp by painting it down one minute early
temps.add(tempDatamap(tb_start, tb_before, now - 1 * 60 * 1000, endBasalValue, tb_amount)); temps.add(tempDatamap(tb_start, tb_before, now - 1 * 60 * 1000, endBasalValue, tb_amount));
} else { } else {
//express currently running temp by painting it a bit into the future //express currently running temp by painting it a bit into the future
Profile profileNow = ProfileFunctions.getInstance().getProfile(now); Profile profileNow = profileFunction.getProfile(now);
double currentAmount = tb2.tempBasalConvertedToAbsolute(now, profileNow); double currentAmount = tb2.tempBasalConvertedToAbsolute(now, profileNow);
if (currentAmount != tb_amount) { if (currentAmount != tb_amount) {
temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount)); temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount));
@ -536,13 +530,13 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); //use "now" to express current situation tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); //use "now" to express current situation
if (tb2 != null) { if (tb2 != null) {
//onset at the end //onset at the end
Profile profileTB = ProfileFunctions.getInstance().getProfile(runningTime); Profile profileTB = profileFunction.getProfile(runningTime);
double currentAmount = tb2.tempBasalConvertedToAbsolute(runningTime, profileTB); double currentAmount = tb2.tempBasalConvertedToAbsolute(runningTime, profileTB);
temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount));
} }
} }
List<Treatment> treatments = TreatmentsPlugin.getPlugin().getTreatmentsFromHistory(); List<Treatment> treatments = treatmentsPlugin.getTreatmentsFromHistory();
for (Treatment treatment : treatments) { for (Treatment treatment : treatments) {
if (treatment.date > startTimeWindow) { if (treatment.date > startTimeWindow) {
boluses.add(treatmentMap(treatment.date, treatment.insulin, treatment.carbs, treatment.isSMB, treatment.isValid)); boluses.add(treatmentMap(treatment.date, treatment.insulin, treatment.carbs, treatment.isSMB, treatment.isValid));
@ -550,8 +544,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
} }
final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; final LoopPlugin.LastRun finalLastRun = loopPlugin.lastRun;
if (SP.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.request.hasPredictions && finalLastRun.constraintsProcessed != null) { if (sp.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.request.hasPredictions && finalLastRun.constraintsProcessed != null) {
List<BgReading> predArray = finalLastRun.constraintsProcessed.getPredictions(); List<BgReading> predArray = finalLastRun.constraintsProcessed.getPredictions();
if (!predArray.isEmpty()) { if (!predArray.isEmpty()) {
@ -649,7 +643,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMapRequest.getDataMap().putString("message", message); dataMapRequest.getDataMap().putString("message", message);
dataMapRequest.getDataMap().putString("actionstring", actionstring); dataMapRequest.getDataMap().putString("actionstring", actionstring);
log.debug("Requesting confirmation from wear: " + actionstring); aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
debugData("sendActionConfirmationRequest", putDataRequest); debugData("sendActionConfirmationRequest", putDataRequest);
@ -669,7 +663,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMapRequest.getDataMap().putString("message", message); dataMapRequest.getDataMap().putString("message", message);
dataMapRequest.getDataMap().putString("actionstring", actionstring); dataMapRequest.getDataMap().putString("actionstring", actionstring);
log.debug("Requesting confirmation from wear: " + actionstring); aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
debugData("sendChangeConfirmationRequest", putDataRequest); debugData("sendChangeConfirmationRequest", putDataRequest);
@ -687,7 +681,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest"); dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest");
dataMapRequest.getDataMap().putString("actionstring", actionstring); dataMapRequest.getDataMap().putString("actionstring", actionstring);
log.debug("Canceling notification on wear: " + actionstring); aapsLogger.debug(LTag.WEAR, "Canceling notification on wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
debugData("sendCancelNotificationRequest", putDataRequest); debugData("sendCancelNotificationRequest", putDataRequest);
@ -700,26 +694,25 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendStatus() { private void sendStatus() {
if (googleApiClient.isConnected()) { if (googleApiClient.isConnected()) {
Profile profile = ProfileFunctions.getInstance().getProfile(); Profile profile = profileFunction.getProfile();
String status = MainApp.gs(R.string.noprofile); String status = resourceHelper.gs(R.string.noprofile);
String iobSum, iobDetail, cobString, currentBasal, bgiString; String iobSum, iobDetail, cobString, currentBasal, bgiString;
iobSum = iobDetail = cobString = currentBasal = bgiString = ""; iobSum = iobDetail = cobString = currentBasal = bgiString = "";
if (profile != null) { if (profile != null) {
TreatmentsInterface treatmentsInterface = TreatmentsPlugin.getPlugin(); treatmentsPlugin.updateTotalIOBTreatments();
treatmentsInterface.updateTotalIOBTreatments(); IobTotal bolusIob = treatmentsPlugin.getLastCalculationTreatments().round();
IobTotal bolusIob = treatmentsInterface.getLastCalculationTreatments().round(); treatmentsPlugin.updateTotalIOBTempBasals();
treatmentsInterface.updateTotalIOBTempBasals(); IobTotal basalIob = treatmentsPlugin.getLastCalculationTempBasals().round();
IobTotal basalIob = treatmentsInterface.getLastCalculationTempBasals().round();
iobSum = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob); iobSum = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob);
iobDetail = "(" + DecimalFormatter.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")"; iobDetail = "(" + DecimalFormatter.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")";
cobString = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "WatcherUpdaterService").generateCOBString(); cobString = iobCobCalculatorPlugin.getCobInfo(false, "WatcherUpdaterService").generateCOBString();
currentBasal = generateBasalString(treatmentsInterface); currentBasal = generateBasalString();
//bgi //bgi
double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), ProfileFunctions.getSystemUnits()); double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.to1Decimal(bgi); bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.to1Decimal(bgi);
status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString);
@ -731,11 +724,11 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
String rigBattery = NSDeviceStatus.getInstance().getUploaderStatus().trim(); String rigBattery = NSDeviceStatus.getInstance().getUploaderStatus().trim();
long openApsStatus = -1; long openApsStatus;
//OpenAPS status //OpenAPS status
if (Config.APS) { if (Config.APS) {
//we are AndroidAPS //we are AndroidAPS
openApsStatus = LoopPlugin.lastRun != null && LoopPlugin.lastRun.lastEnact != null && LoopPlugin.lastRun.lastEnact.getTime() != 0 ? LoopPlugin.lastRun.lastEnact.getTime() : -1; openApsStatus = loopPlugin.lastRun != null && loopPlugin.lastRun.lastEnact != null && loopPlugin.lastRun.lastEnact.getTime() != 0 ? loopPlugin.lastRun.lastEnact.getTime() : -1;
} else { } else {
//NSClient or remote //NSClient or remote
openApsStatus = NSDeviceStatus.getOpenApsTimestamp(); openApsStatus = NSDeviceStatus.getOpenApsTimestamp();
@ -746,14 +739,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMapRequest.getDataMap().putString("externalStatusString", status); dataMapRequest.getDataMap().putString("externalStatusString", status);
dataMapRequest.getDataMap().putString("iobSum", iobSum); dataMapRequest.getDataMap().putString("iobSum", iobSum);
dataMapRequest.getDataMap().putString("iobDetail", iobDetail); dataMapRequest.getDataMap().putString("iobDetail", iobDetail);
dataMapRequest.getDataMap().putBoolean("detailedIob", SP.getBoolean(R.string.key_wear_detailediob, false)); dataMapRequest.getDataMap().putBoolean("detailedIob", sp.getBoolean(R.string.key_wear_detailediob, false));
dataMapRequest.getDataMap().putString("cob", cobString); dataMapRequest.getDataMap().putString("cob", cobString);
dataMapRequest.getDataMap().putString("currentBasal", currentBasal); dataMapRequest.getDataMap().putString("currentBasal", currentBasal);
dataMapRequest.getDataMap().putString("battery", "" + phoneBattery); dataMapRequest.getDataMap().putString("battery", "" + phoneBattery);
dataMapRequest.getDataMap().putString("rigBattery", rigBattery); dataMapRequest.getDataMap().putString("rigBattery", rigBattery);
dataMapRequest.getDataMap().putLong("openApsStatus", openApsStatus); dataMapRequest.getDataMap().putLong("openApsStatus", openApsStatus);
dataMapRequest.getDataMap().putString("bgi", bgiString); dataMapRequest.getDataMap().putString("bgi", bgiString);
dataMapRequest.getDataMap().putBoolean("showBgi", SP.getBoolean(R.string.key_wear_showbgi, false)); dataMapRequest.getDataMap().putBoolean("showBgi", sp.getBoolean(R.string.key_wear_showbgi, false));
dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0); dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
debugData("sendStatus", putDataRequest); debugData("sendStatus", putDataRequest);
@ -766,7 +759,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendPreferences() { private void sendPreferences() {
if (googleApiClient.isConnected()) { if (googleApiClient.isConnected()) {
boolean wearcontrol = SP.getBoolean("wearcontrol", false); boolean wearcontrol = sp.getBoolean("wearcontrol", false);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH);
//unique content //unique content
@ -785,7 +778,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Log.d(TAG, "WR: " + source + " " + data); // Log.d(TAG, "WR: " + source + " " + data);
} }
private void executeTask(AsyncTask task, DataMap... parameters) { private void executeTask(AsyncTask task, DataMap... parameters) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) parameters); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) parameters);
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
@ -802,21 +794,19 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
String status = ""; String status = "";
if (profile == null) { if (profile == null) {
status = MainApp.gs(R.string.noprofile); status = resourceHelper.gs(R.string.noprofile);
return status; return status;
} }
LoopPlugin activeloop = LoopPlugin.getPlugin(); if (!loopPlugin.isEnabled(PluginType.LOOP)) {
status += resourceHelper.gs(R.string.disabledloop) + "\n";
if (!activeloop.isEnabled(PluginType.LOOP)) {
status += MainApp.gs(R.string.disabledloop) + "\n";
lastLoopStatus = false; lastLoopStatus = false;
} else { } else {
lastLoopStatus = true; lastLoopStatus = true;
} }
String iobString = ""; String iobString;
if (SP.getBoolean(R.string.key_wear_detailediob, false)) { if (sp.getBoolean(R.string.key_wear_detailediob, false)) {
iobString = iobSum + " " + iobDetail; iobString = iobSum + " " + iobDetail;
} else { } else {
iobString = iobSum + "U"; iobString = iobSum + "U";
@ -825,7 +815,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
status += currentBasal + " " + iobString; status += currentBasal + " " + iobString;
//add BGI if shown, otherwise return //add BGI if shown, otherwise return
if (SP.getBoolean(R.string.key_wear_showbgi, false)) { if (sp.getBoolean(R.string.key_wear_showbgi, false)) {
status += " " + bgiString; status += " " + bgiString;
} }
@ -833,19 +823,19 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
} }
@NonNull @NonNull
private String generateBasalString(TreatmentsInterface treatmentsInterface) { private String generateBasalString() {
String basalStringResult; String basalStringResult;
Profile profile = ProfileFunctions.getInstance().getProfile(); Profile profile = profileFunction.getProfile();
if (profile == null) if (profile == null)
return ""; return "";
TemporaryBasal activeTemp = treatmentsInterface.getTempBasalFromHistory(System.currentTimeMillis()); TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis());
if (activeTemp != null) { if (activeTemp != null) {
basalStringResult = activeTemp.toStringShort(); basalStringResult = activeTemp.toStringShort();
} else { } else {
if (SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false)) { if (sp.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false)) {
basalStringResult = "100%"; basalStringResult = "100%";
} else { } else {
basalStringResult = DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h"; basalStringResult = DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h";
@ -859,7 +849,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
googleApiClient.disconnect(); googleApiClient.disconnect();
} }
WearPlugin.unRegisterWatchUpdaterService();
} }
@Override @Override
@ -867,7 +856,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
} }
@Override @Override
public void onConnectionFailed(ConnectionResult connectionResult) { public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
} }
public static boolean shouldReportLoopStatus(boolean enabled) { public static boolean shouldReportLoopStatus(boolean enabled) {

View file

@ -1689,5 +1689,6 @@
<string name="clearqueueconfirm">Clear queue? All data in queue will be lost!</string> <string name="clearqueueconfirm">Clear queue? All data in queue will be lost!</string>
<string name="key_xdripstatus_detailediob" translatable="false">xdripstatus_detailediob</string> <string name="key_xdripstatus_detailediob" translatable="false">xdripstatus_detailediob</string>
<string name="key_xdripstatus_showbgi" translatable="false">xdripstatus_showbgi</string> <string name="key_xdripstatus_showbgi" translatable="false">xdripstatus_showbgi</string>
<string name="key_wear_detailed_delta" translatable="false">wear_detailed_delta</string>
</resources> </resources>

View file

@ -67,7 +67,7 @@
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:key="wear_detailed_delta" android:key="@string/key_wear_detailed_delta"
android:summary="@string/wear_detailed_delta_summary" android:summary="@string/wear_detailed_delta_summary"
android:title="@string/wear_detailed_delta_title" /> android:title="@string/wear_detailed_delta_title" />