diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9f7af3102b..bcc1e93abd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -166,6 +166,8 @@ android:name=".plugins.Overview.notifications.DismissNotificationService" android:exported="false" /> + + diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 84bd684cf6..c2db0f270b 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -406,8 +406,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { console.error("SMB disabled (no enableSMB preferences active)"); } - // enable UAM (if enabled in preferences) if SMB is enabled - var enableUAM=(profile.enableUAM && enableSMB); + // enable UAM (if enabled in preferences) + var enableUAM=(profile.enableUAM); //console.error(meal_data); @@ -942,10 +942,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); if (durationReq < 0) { durationReq = 0; - // don't set a temp longer than 120 minutes + // don't set an SMB zero temp longer than 60 minutess } else { durationReq = round(durationReq/30)*30; - durationReq = Math.min(120,Math.max(0,durationReq)); + durationReq = Math.min(60,Math.max(0,durationReq)); } //console.error(durationReq); //rT.reason += "insulinReq " + insulinReq + "; " diff --git a/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java index c308cd2efe..636db14086 100644 --- a/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java @@ -176,13 +176,19 @@ public class HistoryBrowseActivity extends AppCompatActivity { activity.runOnUiThread(new Runnable() { @Override public void run() { - updateGUI("EventAutosensCalculationFinished"); + synchronized (HistoryBrowseActivity.this) { + updateGUI("EventAutosensCalculationFinished"); + } } }); } } void updateGUI(String from) { + + if (noProfile == null || buttonDate == null || buttonZoom == null || bgGraph == null || iobGraph == null || seekBar == null) + return; + final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); final Profile profile = MainApp.getConfigBuilder().getProfile(); diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index c1b616c086..ea8948abdd 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -20,7 +20,6 @@ import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.util.Linkify; -import android.util.Log; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; @@ -42,11 +41,11 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventFeatureRunning; +import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Food.FoodPlugin; -import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.setupwizard.SetupWizardActivity; import info.nightscout.androidaps.tabs.TabPageAdapter; @@ -88,7 +87,8 @@ public class MainActivity extends AppCompatActivity { drawerLayout.addDrawerListener(actionBarDrawerToggle); actionBarDrawerToggle.syncState(); - onStatusEvent(new EventSetWakeLock(SP.getBoolean("lockscreen", false))); + // initialize screen wake lock + onEventPreferenceChange(new EventPreferenceChange(R.string.key_keep_screen_on)); doMigrations(); @@ -156,15 +156,18 @@ public class MainActivity extends AppCompatActivity { } @Subscribe - public void onStatusEvent(final EventSetWakeLock ev) { - final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (ev.lock) { - mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AAPS"); - if (!mWakeLock.isHeld()) - mWakeLock.acquire(); - } else { - if (mWakeLock != null && mWakeLock.isHeld()) - mWakeLock.release(); + public void onEventPreferenceChange(final EventPreferenceChange ev) { + if (ev.isChanged(R.string.key_keep_screen_on)) { + boolean keepScreenOn = SP.getBoolean(R.string.key_keep_screen_on, false); + final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (keepScreenOn) { + mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AAPS"); + if (!mWakeLock.isHeld()) + mWakeLock.acquire(); + } else { + if (mWakeLock != null && mWakeLock.isHeld()) + mWakeLock.release(); + } } } @@ -184,8 +187,8 @@ public class MainActivity extends AppCompatActivity { } } - boolean lockScreen = BuildConfig.NSCLIENTOLNY && SP.getBoolean("lockscreen", false); - if (lockScreen) + boolean keepScreenOn = BuildConfig.NSCLIENTOLNY && SP.getBoolean(R.string.key_keep_screen_on, false); + if (keepScreenOn) getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); else getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index a677b65d6a..24631b10c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -5,6 +5,7 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.os.SystemClock; import android.support.annotation.Nullable; +import android.support.annotation.PluralsRes; import android.support.v4.content.LocalBroadcastManager; import com.crashlytics.android.Crashlytics; @@ -47,8 +48,6 @@ import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAPlugin; import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; import info.nightscout.androidaps.plugins.Overview.OverviewPlugin; -import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.androidaps.plugins.Persistentnotification.PersistentNotificationPlugin; import info.nightscout.androidaps.plugins.ProfileLocal.LocalProfilePlugin; import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; @@ -61,9 +60,10 @@ import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin; import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref1Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.SmsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.Source.SourceGlimpPlugin; @@ -149,6 +149,7 @@ public class MainApp extends Application { pluginsList.add(SensitivityOref0Plugin.getPlugin()); pluginsList.add(SensitivityAAPSPlugin.getPlugin()); pluginsList.add(SensitivityWeightedAveragePlugin.getPlugin()); + pluginsList.add(SensitivityOref1Plugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRPlugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRKoreanPlugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRv2Plugin.getPlugin()); @@ -186,7 +187,7 @@ public class MainApp extends Application { pluginsList.add(WearPlugin.initPlugin(this)); pluginsList.add(StatuslinePlugin.initPlugin(this)); - pluginsList.add(new PersistentNotificationPlugin(this)); + pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin()); pluginsList.add(sConfigBuilder = ConfigBuilderPlugin.getPlugin()); @@ -281,6 +282,10 @@ public class MainApp extends Application { return sResources.getString(id, args); } + public static String gq(@PluralsRes int id, int quantity, Object... args) { + return sResources.getQuantityString(id, quantity, args); + } + public static int gc(int id) { return sResources.getColor(id); } diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index c2d3e8411b..0f50458570 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -31,9 +31,10 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref1Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.SmsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.Wear.WearPlugin; @@ -153,6 +154,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(SensitivityAAPSPlugin.getPlugin(), PluginType.SENSITIVITY); addPreferencesFromResourceIfEnabled(SensitivityWeightedAveragePlugin.getPlugin(), PluginType.SENSITIVITY); addPreferencesFromResourceIfEnabled(SensitivityOref0Plugin.getPlugin(), PluginType.SENSITIVITY); + addPreferencesFromResourceIfEnabled(SensitivityOref1Plugin.getPlugin(), PluginType.SENSITIVITY); if (Config.HWPUMPS) { addPreferencesFromResourceIfEnabled(DanaRPlugin.getPlugin(), PluginType.PUMP); diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index f8a9ed5d5e..9f3873cb93 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -604,6 +604,7 @@ public class DataService extends IntentService { NSSgv nsSgv = new NSSgv(sgvJson); BgReading bgReading = new BgReading(nsSgv); MainApp.getDbHelper().createIfNotExists(bgReading, "NS"); + SourceNSClientPlugin.getPlugin().detectSource(JsonHelper.safeGetString(sgvJson, "device"), JsonHelper.safeGetLong(sgvJson, "mills")); } private void handleNewSMS(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java index 3161cb19f3..bfbbfedbd1 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java @@ -4,12 +4,10 @@ import java.util.ArrayList; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; /** * Created by mike on 19.03.2018. @@ -25,7 +23,7 @@ public class ConstraintChecker implements ConstraintsInterface { public Constraint isLoopInvokationAllowed() { - return isLoopInvokationAllowed(new Constraint<>(true)); + return isLoopInvocationAllowed(new Constraint<>(true)); } public Constraint isClosedLoopAllowed() { @@ -69,13 +67,13 @@ public class ConstraintChecker implements ConstraintsInterface { } @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { ArrayList constraintsPlugins = mainApp.getSpecificPluginsListByInterface(ConstraintsInterface.class); for (PluginBase p : constraintsPlugins) { ConstraintsInterface constraint = (ConstraintsInterface) p; if (!p.isEnabled(PluginType.CONSTRAINTS)) continue; - constraint.isLoopInvokationAllowed(value); + constraint.isLoopInvocationAllowed(value); } return value; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java index c2f9d16dc3..6e65e6d6d3 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java +++ b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java @@ -10,6 +10,7 @@ import java.util.Date; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; /** * Created by mike on 29.05.2017. @@ -17,6 +18,7 @@ import info.nightscout.androidaps.db.Source; public class DetailedBolusInfo { public long date = System.currentTimeMillis(); + public long lastKnownBolusTime; public String eventType = CareportalEvent.MEALBOLUS; public double insulin = 0; public double carbs = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 844fa6472a..7412f8ce4c 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -202,6 +202,12 @@ public class Profile { if (targetHigh_v == null) targetHigh_v = convertToSparseArray(targetHigh); validate(targetHigh_v); + + if (targetHigh_v.size() != targetLow_v.size()) isValid = false; + else for (int i = 0; i < targetHigh_v.size(); i++) + if (targetHigh_v.valueAt(i) < targetLow_v.valueAt(i)) + isValid = false; + isValidated = true; } @@ -239,16 +245,16 @@ public class Profile { isValidated = false; } - } + } return isValid; } protected void sendBelowMinimumNotification(String from) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); } protected void sendAboveMaximumNotification(String from) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.maximumbasalvaluereplaced), from), Notification.NORMAL))); + MainApp.bus().post(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.maximumbasalvaluereplaced), from), Notification.NORMAL))); } private void validate(LongSparseArray array) { @@ -452,12 +458,12 @@ public class Profile { return ret; } - public double getTarget(){ - return getTarget(secondsFromMidnight(System.currentTimeMillis())); + public double getTarget() { + return getTarget(secondsFromMidnight(System.currentTimeMillis())); } protected double getTarget(int timeAsSeconds) { - return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds))/2; + return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2; } public double getTargetLow() { @@ -552,6 +558,12 @@ public class Profile { else return DecimalFormatter.to1Decimal(valueInMmol); } + public static String toSignedUnitsString(Double valueInMgdl, Double valueInMmol, String units) { + if (units.equals(Constants.MGDL)) + return (valueInMgdl > 0 ? "+" : "") + DecimalFormatter.to0Decimal(valueInMgdl); + else return (valueInMmol > 0 ? "+" : "") + DecimalFormatter.to1Decimal(valueInMmol); + } + // targets are stored in mg/dl but profile vary public static String toTargetRangeString(double low, double high, String sourceUnits, String units) { double lowMgdl = toMgdl(low, sourceUnits); diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index a963158f2f..747391404a 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -24,15 +24,17 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSMbg; import info.nightscout.androidaps.plugins.Overview.OverviewFragment; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.T; import info.nightscout.utils.Translator; @DatabaseTable(tableName = DatabaseHelper.DATABASE_CAREPORTALEVENTS) -public class CareportalEvent implements DataPointWithLabelInterface { +public class CareportalEvent implements DataPointWithLabelInterface, Interval { private static Logger log = LoggerFactory.getLogger(CareportalEvent.class); @DatabaseField(id = true) @@ -127,6 +129,19 @@ public class CareportalEvent implements DataPointWithLabelInterface { return result; } + + public static boolean isEvent5minBack(List list, long time) { + for (int i = 0; i < list.size(); i++) { + CareportalEvent event = list.get(i); + if (event.date <= time && event.date > (time - T.mins(5).msecs())) { + //log.debug("Found event for time: " + DateUtil.dateAndTimeString(time) + " " + event.toString()); + return true; + } + } + //log.debug("WWWWWW No found event for time: " + DateUtil.dateAndTimeString(time)); + return false; + } + // -------- DataPointWithLabelInterface ------- @Override @@ -207,14 +222,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { @Override public long getDuration() { - try { - JSONObject object = new JSONObject(json); - if (object.has("duration")) - return object.getInt("duration") * 60 * 1000L; - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return 0; + return end() - start(); } @Override @@ -253,8 +261,79 @@ public class CareportalEvent implements DataPointWithLabelInterface { if (eventType.equals(EXERCISE)) return Color.BLUE; if (eventType.equals(OPENAPSOFFLINE)) - return Color.GRAY; + return Color.GRAY & 0x80FFFFFF; return Color.GRAY; } + // Interval interface + Long cuttedEnd = null; + + @Override + public long durationInMsec() { + try { + JSONObject object = new JSONObject(json); + if (object.has("duration")) + return object.getInt("duration") * 60 * 1000L; + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + return 0; + } + + @Override + public long start() { + return date; + } + + @Override + public long originalEnd() { + return date + durationInMsec(); + } + + @Override + public long end() { + if (cuttedEnd != null) + return cuttedEnd; + return originalEnd(); + } + + @Override + public void cutEndTo(long end) { + cuttedEnd = end; + } + + @Override + public boolean match(long time) { + if (start() <= time && end() >= time) + return true; + return false; + } + + public boolean before(long time) { + if (end() < time) + return true; + return false; + } + + public boolean after(long time) { + if (start() > time) + return true; + return false; + } + + @Override + public boolean isInProgress() { + return match(System.currentTimeMillis()); + } + + @Override + public boolean isEndingEvent() { + return durationInMsec() == 0; + } + + @Override + public boolean isValid() { + return eventType.equals(OPENAPSOFFLINE); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 7174c19ec4..5095195872 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.OverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventCareportalEventChange; @@ -1242,6 +1243,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1249,13 +1251,41 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } - public List getCareportalEventsFromTime(boolean ascending) { + public void preprocessOpenAPSOfflineEvents(List list) { + OverlappingIntervals offlineEvents = new OverlappingIntervals(); + for (int i = 0; i < list.size(); i++) { + CareportalEvent event = list.get(i); + if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) continue; + offlineEvents.add(event); + } + + } + + public List getCareportalEventsFromTime(long mills, String type, boolean ascending) { + try { + List careportalEvents; + QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.ge("date", mills).and().eq("eventType", type); + PreparedQuery preparedQuery = queryBuilder.prepare(); + careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); + return careportalEvents; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); + } + + public List getCareportalEvents(boolean ascending) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1470,14 +1500,19 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { profileSwitch.profileJson = trJson.getString("profileJson"); else { ProfileStore store = MainApp.getConfigBuilder().getActiveProfileInterface().getProfile(); - Profile profile = store.getSpecificProfile(profileSwitch.profileName); - if (profile != null) { - profileSwitch.profileJson = profile.getData().toString(); - log.debug("Profile switch prefilled with JSON from local store"); - // Update data in NS - NSUpload.updateProfileSwitch(profileSwitch); + if (store != null) { + Profile profile = store.getSpecificProfile(profileSwitch.profileName); + if (profile != null) { + profileSwitch.profileJson = profile.getData().toString(); + log.debug("Profile switch prefilled with JSON from local store"); + // Update data in NS + NSUpload.updateProfileSwitch(profileSwitch); + } else { + log.debug("JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); + return; + } } else { - log.debug("JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); + log.debug("Store for profile switch doesn't exist. Ignoring: " + trJson.toString()); return; } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java index 6022525afb..40c8e50c6d 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java @@ -103,14 +103,11 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { if(LocalProfilePlugin.LOCAL_PROFILE.equals(name)){ name = DecimalFormatter.to2Decimal(getProfileObject().percentageBasalSum()) + "U "; } - //Test if name is already containing percentage or timeshift - if (!name.endsWith("h)") || !name.endsWith("%)")) { - if (isCPP) { - name += "(" + percentage + "%"; - if (timeshift != 0) - name += "," + timeshift + "h"; - name += ")"; - } + if (isCPP) { + name += "(" + percentage + "%"; + if (timeshift != 0) + name += "," + timeshift + "h"; + name += ")"; } return name; } diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java index 03df71f31b..57050bdcc9 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.events; + +import info.nightscout.utils.StringUtils; + public class EventNetworkChange extends Event { public boolean mobileConnected = false; @@ -9,6 +12,6 @@ public class EventNetworkChange extends Event { public boolean roaming = false; public String getSsid() { - return ssid.replace("SSID: ","").replaceAll("\"",""); + return StringUtils.removeSurroundingQuotes(ssid); } } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java index a1daa08a56..820170dc0f 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java @@ -7,7 +7,7 @@ import info.nightscout.androidaps.data.Profile; */ public interface ConstraintsInterface { - default Constraint isLoopInvokationAllowed(Constraint value) { + default Constraint isLoopInvocationAllowed(Constraint value) { return value; } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java index ed9b5672a2..9f3e4fe5c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -1,11 +1,13 @@ package info.nightscout.androidaps.interfaces; import android.os.SystemClock; +import android.support.v4.app.FragmentActivity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; /** @@ -32,6 +34,12 @@ public abstract class PluginBase { this.pluginDescription = pluginDescription; } + // Default always calls invoke + // Plugins that have special constraints if they get switched to may override this method + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) { + pluginSwitcher.invoke(); + } + // public PluginType getType() { // return mainType; // } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java index deb649c21f..25dab4494b 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java @@ -1,11 +1,28 @@ package info.nightscout.androidaps.interfaces; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; /** * Created by mike on 24.06.2017. */ public interface SensitivityInterface { + + double MIN_HOURS = 1; + double MIN_HOURS_FULL_AUTOSENS = 4; + AutosensResult detectSensitivity(long fromTime, long toTime); + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java index e91ad8079f..3e60ca2b77 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java @@ -4,6 +4,7 @@ package info.nightscout.androidaps.plugins.ConfigBuilder; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.StringRes; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,9 +23,7 @@ import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; -import butterknife.OnCheckedChanged; import butterknife.OnClick; -import butterknife.Optional; import butterknife.Unbinder; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.PreferencesActivity; @@ -44,35 +43,17 @@ import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.Insulin.InsulinOrefRapidActingPlugin; import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.PasswordProtection; public class ConfigBuilderFragment extends SubscriberFragment { - private List pluginViews = new ArrayList<>(); + private List pluginViewHolders = new ArrayList<>(); - @BindView(R.id.profile_plugins) - LinearLayout profilePlugins; - @BindView(R.id.insulin_plugins) - LinearLayout insulinPlugins; - @BindView(R.id.bgsource_plugins) - LinearLayout bgSourcePlugins; - @BindView(R.id.pump_plugins) - LinearLayout pumpPlugins; - @BindView(R.id.sensitivity_plugins) - LinearLayout sensitivityPlugins; - @BindView(R.id.aps_plugins) - LinearLayout apsPlugins; - @BindView(R.id.loop_plugins) - LinearLayout loopPlugins; - @BindView(R.id.constraints_plugins) - LinearLayout constraintsPlugins; - @BindView(R.id.treatments_plugins) - LinearLayout treatmentsPlugins; - @BindView(R.id.general_plugins) - LinearLayout generalPlugins; + @BindView(R.id.categories) + LinearLayout categories; @BindView(R.id.main_layout) ScrollView mainLayout; @@ -111,34 +92,40 @@ public class ConfigBuilderFragment extends SubscriberFragment { @Override public void onDestroyView() { super.onDestroyView(); - for (PluginView pluginView : pluginViews) pluginView.unbind(); - pluginViews.clear(); + for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.unbind(); + pluginViewHolders.clear(); } @Override protected void updateGUI() { - for (PluginView pluginView : pluginViews) pluginView.update(); + for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.update(); } private void createViews() { - createViewsForPlugins(profilePlugins, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface.class, PluginType.PROFILE)); - createViewsForPlugins(insulinPlugins, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface.class, PluginType.INSULIN)); - createViewsForPlugins(bgSourcePlugins, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface.class, PluginType.BGSOURCE)); - createViewsForPlugins(pumpPlugins, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP)); - createViewsForPlugins(sensitivityPlugins, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginType.SENSITIVITY)); - createViewsForPlugins(apsPlugins, MainApp.getSpecificPluginsVisibleInList(PluginType.APS)); - createViewsForPlugins(loopPlugins, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP)); - createViewsForPlugins(constraintsPlugins, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginType.CONSTRAINTS)); - createViewsForPlugins(treatmentsPlugins, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT)); - createViewsForPlugins(generalPlugins, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL)); + createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface.class, PluginType.PROFILE)); + createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface.class, PluginType.INSULIN)); + createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface.class, PluginType.BGSOURCE)); + createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP)); + createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginType.SENSITIVITY)); + createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, MainApp.getSpecificPluginsVisibleInList(PluginType.APS)); + createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP)); + createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginType.CONSTRAINTS)); + createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT)); + createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL)); } - private void createViewsForPlugins(LinearLayout parent, List plugins) { + private void createViewsForPlugins(@StringRes int title, @StringRes int description, PluginType pluginType, List plugins) { + if (plugins.size() == 0) return; + LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_category, null); + ((TextView) parent.findViewById(R.id.category_title)).setText(MainApp.gs(title)); + ((TextView) parent.findViewById(R.id.category_description)).setText(MainApp.gs(description)); + LinearLayout pluginContainer = parent.findViewById(R.id.category_plugins); for (PluginBase plugin: plugins) { - PluginView pluginView = new PluginView(plugin); - parent.addView(pluginView.getBaseView()); - pluginViews.add(pluginView); + PluginViewHolder pluginViewHolder = new PluginViewHolder(pluginType, plugin); + pluginContainer.addView(pluginViewHolder.getBaseView()); + pluginViewHolders.add(pluginViewHolder); } + categories.addView(parent); } private boolean areMultipleSelectionsAllowed(PluginType type) { @@ -200,9 +187,10 @@ public class ConfigBuilderFragment extends SubscriberFragment { } } - class PluginView { + public class PluginViewHolder { private Unbinder unbinder; + private PluginType pluginType; private PluginBase plugin; LinearLayout baseView; @@ -219,7 +207,8 @@ public class ConfigBuilderFragment extends SubscriberFragment { @BindView(R.id.plugin_visibility) CheckBox pluginVisibility; - public PluginView(PluginBase plugin) { + public PluginViewHolder(PluginType pluginType, PluginBase plugin) { + this.pluginType = pluginType; this.plugin = plugin; baseView = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_plugin, null); unbinder = ButterKnife.bind(this, baseView); @@ -231,10 +220,10 @@ public class ConfigBuilderFragment extends SubscriberFragment { } public void update() { - enabledExclusive.setVisibility(areMultipleSelectionsAllowed(plugin.getType()) ? View.GONE : View.VISIBLE); - enabledInclusive.setVisibility(areMultipleSelectionsAllowed(plugin.getType()) ? View.VISIBLE : View.GONE); - enabledExclusive.setChecked(plugin.isEnabled(plugin.getType())); - enabledInclusive.setChecked(plugin.isEnabled(plugin.getType())); + enabledExclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.GONE : View.VISIBLE); + enabledInclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.VISIBLE : View.GONE); + enabledExclusive.setChecked(plugin.isEnabled(pluginType)); + enabledInclusive.setChecked(plugin.isEnabled(pluginType)); enabledInclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); enabledExclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); pluginName.setText(plugin.getName()); @@ -243,15 +232,15 @@ public class ConfigBuilderFragment extends SubscriberFragment { pluginDescription.setVisibility(View.VISIBLE); pluginDescription.setText(plugin.getDescription()); } - pluginPreferences.setVisibility(plugin.getPreferencesId() == -1 || !plugin.isEnabled(plugin.getType()) ? View.INVISIBLE : View.VISIBLE); + pluginPreferences.setVisibility(plugin.getPreferencesId() == -1 || !plugin.isEnabled(pluginType) ? View.INVISIBLE : View.VISIBLE); pluginVisibility.setVisibility(plugin.hasFragment() ? View.VISIBLE : View.INVISIBLE); - pluginVisibility.setEnabled(!(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwayVisible) && plugin.isEnabled(plugin.getType())); + pluginVisibility.setEnabled(!(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwayVisible) && plugin.isEnabled(pluginType)); pluginVisibility.setChecked(plugin.isFragmentVisible()); } @OnClick(R.id.plugin_visibility) void onVisibilityChanged() { - plugin.setFragmentVisible(plugin.getType(), pluginVisibility.isChecked()); + plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked()); ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxVisible"); MainApp.bus().post(new EventRefreshGui()); ConfigBuilderPlugin.getPlugin().logPluginStatus(); @@ -259,16 +248,7 @@ public class ConfigBuilderFragment extends SubscriberFragment { @OnClick({R.id.plugin_enabled_exclusive, R.id.plugin_enabled_inclusive}) void onEnabledChanged() { - boolean enabled = enabledExclusive.getVisibility() == View.VISIBLE ? enabledExclusive.isChecked() : enabledInclusive.isChecked(); - plugin.setPluginEnabled(plugin.getType(), enabled); - plugin.setFragmentVisible(plugin.getType(), enabled); - processOnEnabledCategoryChanged(plugin, plugin.getType()); - updateGUI(); - ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); - MainApp.bus().post(new EventRefreshGui()); - MainApp.bus().post(new EventConfigBuilderChange()); - ConfigBuilderPlugin.getPlugin().logPluginStatus(); - FabricPrivacy.getInstance().logCustom(new CustomEvent("ConfigurationChange")); + plugin.switchAllowed(new PluginSwitcher(), getActivity()); } @OnClick(R.id.plugin_preferences) @@ -284,6 +264,23 @@ public class ConfigBuilderFragment extends SubscriberFragment { unbinder.unbind(); } - } + public class PluginSwitcher { + public void invoke() { + boolean enabled = enabledExclusive.getVisibility() == View.VISIBLE ? enabledExclusive.isChecked() : enabledInclusive.isChecked(); + plugin.setPluginEnabled(pluginType, enabled); + plugin.setFragmentVisible(pluginType, enabled); + processOnEnabledCategoryChanged(plugin, pluginType); + updateGUI(); + ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); + MainApp.bus().post(new EventRefreshGui()); + MainApp.bus().post(new EventConfigBuilderChange()); + ConfigBuilderPlugin.getPlugin().logPluginStatus(); + FabricPrivacy.getInstance().logCustom(new CustomEvent("ConfigurationChange")); + } + public void cancel(){ + updateGUI(); + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 1911f588b1..886eeb3db7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -43,7 +43,7 @@ import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.CommandQueue; import info.nightscout.utils.FabricPrivacy; @@ -113,9 +113,17 @@ public class ConfigBuilderPlugin extends PluginBase { pluginList = MainApp.getPluginsList(); upgradeSettings(); loadSettings(); + setAlwaysEnabledPluginsEnabled(); MainApp.bus().post(new EventAppInitialized()); } + private void setAlwaysEnabledPluginsEnabled() { + for (PluginBase plugin : pluginList) { + if (plugin.pluginDescription.alwaysEnabled) plugin.setPluginEnabled(plugin.getType(), true); + } + storeSettings("setAlwaysEnabledPluginsEnabled"); + } + public void storeSettings(String from) { if (pluginList != null) { if (Config.logPrefsChange) @@ -520,6 +528,7 @@ public class ConfigBuilderPlugin extends PluginBase { // deliver SMB DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.lastKnownBolusTime = activeTreatments.getLastBolusTime(); detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; detailedBolusInfo.insulin = request.smb; detailedBolusInfo.isSMB = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java index 4f2cfa65e0..2f896ba91b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java @@ -1,12 +1,18 @@ package info.nightscout.androidaps.plugins.ConstraintsObjectives; +import android.animation.LayoutTransition; import android.app.Activity; -import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.CardView; import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; +import android.text.Html; +import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,199 +22,40 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.squareup.otto.Subscribe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventConfigBuilderChange; +import info.nightscout.androidaps.events.EventNewBG; +import info.nightscout.androidaps.events.EventProfileSwitchChange; +import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective; import info.nightscout.utils.FabricPrivacy; -import info.nightscout.utils.T; public class ObjectivesFragment extends SubscriberFragment { private static Logger log = LoggerFactory.getLogger(ObjectivesFragment.class); RecyclerView recyclerView; - LinearLayoutManager llm; CheckBox enableFake; - LinearLayout fake_layout; TextView reset; + ObjectivesAdapter objectivesAdapter = new ObjectivesAdapter(); + Handler handler = new Handler(Looper.getMainLooper()); - public class RecyclerViewAdapter extends RecyclerView.Adapter { - - List objectives; - - RecyclerViewAdapter(List objectives) { - this.objectives = objectives; - } - + private Runnable objectiveUpdater = new Runnable() { @Override - public ObjectiveViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.objectives_item, viewGroup, false); - return new ObjectiveViewHolder(v); + public void run() { + handler.postDelayed(this, 60 * 1000); + updateGUI(); } - - @Override - public void onBindViewHolder(ObjectiveViewHolder holder, int position) { - ObjectivesPlugin.Objective o = objectives.get(position); - ObjectivesPlugin.RequirementResult requirementsMet = ObjectivesPlugin.getPlugin().requirementsMet(position); - Context context = MainApp.instance().getApplicationContext(); - holder.position.setText(String.valueOf(position + 1)); - holder.objective.setText(o.objective); - holder.gate.setText(o.gate); - holder.duration.setText(MainApp.gs(R.string.objectives_minimalduration) + " " + o.durationInDays + " " + MainApp.gs(R.string.days)); - holder.progress.setText(requirementsMet.comment); - holder.started.setText(o.started.toLocaleString()); - holder.accomplished.setText(o.accomplished.toLocaleString()); - - holder.startButton.setTag(o); - holder.verifyButton.setTag(o); - - holder.startButton.setOnClickListener(v -> { - ObjectivesPlugin.Objective o1 = (ObjectivesPlugin.Objective) v.getTag(); - o1.started = new Date(); - updateGUI(); - ObjectivesPlugin.saveProgress(); - }); - holder.verifyButton.setOnClickListener(v -> { - ObjectivesPlugin.Objective o12 = (ObjectivesPlugin.Objective) v.getTag(); - if (ObjectivesPlugin.getPlugin().requirementsMet(o12.num).done || enableFake.isChecked()) { - o12.accomplished = new Date(); - updateGUI(); - ObjectivesPlugin.saveProgress(); - } - }); - - long prevObjectiveAccomplishedTime = position > 0 ? - objectives.get(position - 1).accomplished.getTime() : -1; - - int phase = modifyVisibility(position, prevObjectiveAccomplishedTime, - o.started.getTime(), o.durationInDays, - o.accomplished.getTime(), requirementsMet.done, enableFake.isChecked()); - - switch (phase) { - case 0: - // Phase 0: previous not completed - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - break; - case 1: - // Phase 1: not started - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - holder.started.setVisibility(View.GONE); - break; - case 2: - // Phase 2: started, waiting for duration and met requirements - holder.startButton.setEnabled(false); - holder.verifyLayout.setVisibility(View.GONE); - break; - case 3: - // Phase 3: started, after duration, requirements met - holder.startButton.setEnabled(false); - holder.accomplished.setVisibility(View.INVISIBLE); - break; - case 4: - // Phase 4: verified - holder.gateLayout.setVisibility(View.GONE); - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyButton.setVisibility(View.INVISIBLE); - break; - default: - // should not happen - } - } - - - @Override - public int getItemCount() { - return objectives.size(); - } - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - public class ObjectiveViewHolder extends RecyclerView.ViewHolder { - CardView cv; - TextView position; - TextView objective; - LinearLayout gateLayout; - TextView gate; - TextView duration; - LinearLayout durationLayout; - TextView progress; - LinearLayout progressLayout; - TextView started; - Button startButton; - LinearLayout startedLayout; - TextView accomplished; - Button verifyButton; - LinearLayout verifyLayout; - - ObjectiveViewHolder(View itemView) { - super(itemView); - cv = (CardView) itemView.findViewById(R.id.objectives_cardview); - position = (TextView) itemView.findViewById(R.id.objectives_position); - objective = (TextView) itemView.findViewById(R.id.objectives_objective); - durationLayout = (LinearLayout) itemView.findViewById(R.id.objectives_duration_linearlayout); - duration = (TextView) itemView.findViewById(R.id.objectives_duration); - progressLayout = (LinearLayout) itemView.findViewById(R.id.objectives_progresslayout); - progress = (TextView) itemView.findViewById(R.id.objectives_progress); - gateLayout = (LinearLayout) itemView.findViewById(R.id.objectives_gate_linearlayout); - gate = (TextView) itemView.findViewById(R.id.objectives_gate); - startedLayout = (LinearLayout) itemView.findViewById(R.id.objectives_start_linearlayout); - started = (TextView) itemView.findViewById(R.id.objectives_started); - startButton = (Button) itemView.findViewById(R.id.objectives_start); - verifyLayout = (LinearLayout) itemView.findViewById(R.id.objectives_verify_linearlayout); - accomplished = (TextView) itemView.findViewById(R.id.objectives_accomplished); - verifyButton = (Button) itemView.findViewById(R.id.objectives_verify); - } - } - } - - /** - * returns an int, which represents the phase the current objective is at. - * - * this is mainly used for unit-testing the conditions - * - * @param currentPosition - * @param prevObjectiveAccomplishedTime - * @param objectiveStartedTime - * @param durationInDays - * @param objectiveAccomplishedTime - * @param requirementsMet - * @return - */ - public int modifyVisibility(int currentPosition, - long prevObjectiveAccomplishedTime, - long objectiveStartedTime, int durationInDays, - long objectiveAccomplishedTime, boolean requirementsMet, - boolean enableFakeValue) { - Long now = System.currentTimeMillis(); - if (currentPosition > 0 && prevObjectiveAccomplishedTime == 0) { - return 0; - } else if (objectiveStartedTime == 0) { - return 1; - } else if (objectiveStartedTime > 0 && !enableFakeValue - && objectiveAccomplishedTime == 0 - && !(objectiveStartedTime + T.days(durationInDays).msecs() < now && requirementsMet)) { - return 2; - } else if (objectiveAccomplishedTime == 0) { - return 3; - } else { - return 4; - } - } + }; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -216,45 +63,20 @@ public class ObjectivesFragment extends SubscriberFragment { try { View view = inflater.inflate(R.layout.objectives_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.objectives_recyclerview); - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); - recyclerView.setLayoutManager(llm); - enableFake = (CheckBox) view.findViewById(R.id.objectives_fake); - fake_layout = (LinearLayout) view.findViewById(R.id.objectives_fake_layout); - reset = (TextView) view.findViewById(R.id.objectives_reset); - enableFake.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - updateGUI(); - } + recyclerView = view.findViewById(R.id.objectives_recyclerview); + recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext())); + recyclerView.setAdapter(objectivesAdapter); + enableFake = view.findViewById(R.id.objectives_fake); + reset = view.findViewById(R.id.objectives_reset); + enableFake.setOnClickListener(v -> updateGUI()); + reset.setOnClickListener(v -> { + ObjectivesPlugin.getPlugin().reset(); + ObjectivesPlugin.saveProgress(); + recyclerView.getAdapter().notifyDataSetChanged(); + scrollToCurrentObjective(); }); - reset.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - ObjectivesPlugin.getPlugin().initializeData(); - ObjectivesPlugin.saveProgress(); - updateGUI(); - } - }); - - // Add correct translations to array after app is initialized - ObjectivesPlugin.objectives.get(0).objective = MainApp.gs(R.string.objectives_0_objective); - ObjectivesPlugin.objectives.get(1).objective = MainApp.gs(R.string.objectives_1_objective); - ObjectivesPlugin.objectives.get(2).objective = MainApp.gs(R.string.objectives_2_objective); - ObjectivesPlugin.objectives.get(3).objective = MainApp.gs(R.string.objectives_3_objective); - ObjectivesPlugin.objectives.get(4).objective = MainApp.gs(R.string.objectives_4_objective); - ObjectivesPlugin.objectives.get(5).objective = MainApp.gs(R.string.objectives_5_objective); - ObjectivesPlugin.objectives.get(6).objective = MainApp.gs(R.string.objectives_6_objective); - ObjectivesPlugin.objectives.get(7).objective = MainApp.gs(R.string.objectives_7_objective); - ObjectivesPlugin.objectives.get(0).gate = MainApp.gs(R.string.objectives_0_gate); - ObjectivesPlugin.objectives.get(1).gate = MainApp.gs(R.string.objectives_1_gate); - ObjectivesPlugin.objectives.get(2).gate = MainApp.gs(R.string.objectives_2_gate); - ObjectivesPlugin.objectives.get(3).gate = MainApp.gs(R.string.objectives_3_gate); - ObjectivesPlugin.objectives.get(4).gate = MainApp.gs(R.string.objectives_4_gate); - ObjectivesPlugin.objectives.get(5).gate = MainApp.gs(R.string.objectives_5_gate); - ObjectivesPlugin.objectives.get(7).gate = MainApp.gs(R.string.objectives_7_gate); - - updateGUI(); - + scrollToCurrentObjective(); + startUpdateTimer(); return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -263,13 +85,142 @@ public class ObjectivesFragment extends SubscriberFragment { return null; } + @Override + public synchronized void onDestroyView() { + super.onDestroyView(); + handler.removeCallbacks(objectiveUpdater); + } + + private void startUpdateTimer() { + handler.removeCallbacks(objectiveUpdater); + for (Objective objective : ObjectivesPlugin.getObjectives()) { + if (objective.isStarted() && !objective.isAccomplished()) { + long timeTillNextMinute = (System.currentTimeMillis() - objective.getStartedOn().getTime()) % (60 * 1000); + handler.postDelayed(objectiveUpdater, timeTillNextMinute); + break; + } + } + } + + private void scrollToCurrentObjective() { + for (int i = 0; i < ObjectivesPlugin.getObjectives().size(); i++) { + Objective objective = ObjectivesPlugin.getObjectives().get(i); + if (!objective.isStarted() || !objective.isAccomplished()) { + RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) { + @Override + protected int getVerticalSnapPreference() { + return LinearSmoothScroller.SNAP_TO_START; + } + + @Override + protected int calculateTimeForScrolling(int dx) { + return super.calculateTimeForScrolling(dx) * 4; + } + }; + smoothScroller.setTargetPosition(i); + recyclerView.getLayoutManager().startSmoothScroll(smoothScroller); + break; + } + } + } + + private class ObjectivesAdapter extends RecyclerView.Adapter { + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.objectives_item, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Objective objective = ObjectivesPlugin.getObjectives().get(position); + holder.title.setText(MainApp.gs(R.string.nth_objective, position + 1)); + if (objective.getObjective() != 0) { + holder.objective.setVisibility(View.VISIBLE); + holder.objective.setText(MainApp.gs(objective.getObjective())); + } else holder.objective.setVisibility(View.GONE); + if (objective.getGate() != 0) { + holder.gate.setVisibility(View.VISIBLE); + holder.gate.setText(MainApp.gs(objective.getGate())); + } else holder.gate.setVisibility(View.GONE); + if (!objective.isStarted()) { + holder.gate.setTextColor(0xFFFFFFFF); + holder.verify.setVisibility(View.GONE); + holder.progress.setVisibility(View.GONE); + if (position == 0 || ObjectivesPlugin.getObjectives().get(position - 1).isAccomplished()) + holder.start.setVisibility(View.VISIBLE); + else holder.start.setVisibility(View.GONE); + } else if (objective.isAccomplished()) { + holder.gate.setTextColor(0xFF4CAF50); + holder.verify.setVisibility(View.GONE); + holder.progress.setVisibility(View.GONE); + holder.start.setVisibility(View.GONE); + } else if (objective.isStarted()) { + holder.gate.setTextColor(0xFFFFFFFF); + holder.verify.setVisibility(View.VISIBLE); + holder.verify.setEnabled(objective.isCompleted() || enableFake.isChecked()); + holder.start.setVisibility(View.GONE); + holder.progress.setVisibility(View.VISIBLE); + holder.progress.removeAllViews(); + for (Objective.Task task : objective.getTasks()) { + if (task.shouldBeIgnored()) continue; + TextView textView = new TextView(holder.progress.getContext()); + textView.setTextColor(0xFFFFFFFF); + String basicHTML = "%2$s: %3$s"; + String formattedHTML = String.format(basicHTML, task.isCompleted() ? "#4CAF50" : "#FF9800", MainApp.gs(task.getTask()), task.getProgress()); + textView.setText(Html.fromHtml(formattedHTML)); + holder.progress.addView(textView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + } + } + holder.verify.setOnClickListener((view) -> { + objective.setAccomplishedOn(new Date()); + notifyDataSetChanged(); + scrollToCurrentObjective(); + startUpdateTimer(); + }); + holder.start.setOnClickListener((view) -> { + objective.setStartedOn(new Date()); + notifyDataSetChanged(); + scrollToCurrentObjective(); + startUpdateTimer(); + }); + } + + @Override + public int getItemCount() { + return ObjectivesPlugin.getObjectives().size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + + public CardView cardView; + public TextView title; + public TextView objective; + public TextView gate; + public LinearLayout progress; + public Button verify; + public Button start; + + public ViewHolder(View itemView) { + super(itemView); + cardView = (CardView) itemView; + title = itemView.findViewById(R.id.objective_title); + objective = itemView.findViewById(R.id.objective_objective); + gate = itemView.findViewById(R.id.objective_gate); + progress = itemView.findViewById(R.id.objective_progress); + verify = itemView.findViewById(R.id.objective_verify); + start = itemView.findViewById(R.id.objective_start); + } + } + } + @Override public void updateGUI() { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - RecyclerViewAdapter adapter = new RecyclerViewAdapter(ObjectivesPlugin.objectives); - recyclerView.setAdapter(adapter); + objectivesAdapter.notifyDataSetChanged(); }); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java index 010bb1277a..a67f694eb8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java @@ -1,20 +1,14 @@ package info.nightscout.androidaps.plugins.ConstraintsObjectives; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Date; import java.util.List; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; @@ -23,12 +17,15 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved; -import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; -import info.nightscout.androidaps.plugins.Loop.LoopPlugin; -import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; -import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; -import info.nightscout.utils.DateUtil; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective1; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective2; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective3; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective4; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective5; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective6; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective7; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective8; import info.nightscout.utils.SP; /** @@ -39,6 +36,11 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface private static ObjectivesPlugin objectivesPlugin; + public static List objectives = new ArrayList<>(); + public static boolean bgIsAvailableInNS = false; + public static boolean pumpStatusIsAvailableInNS = false; + public static Integer manualEnacts = 0; + public static ObjectivesPlugin getPlugin() { if (objectivesPlugin == null) { objectivesPlugin = new ObjectivesPlugin(); @@ -46,8 +48,6 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface return objectivesPlugin; } - public static List objectives; - private ObjectivesPlugin() { super(new PluginDescription() .mainType(PluginType.CONSTRAINTS) @@ -58,7 +58,7 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface .shortName(R.string.objectives_shortname) .description(R.string.description_objectives) ); - initializeData(); + setupObjectives(); loadProgress(); } @@ -68,187 +68,38 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface return pump == null || pump.getPumpDescription().isTempBasalCapable; } - public class Objective { - Integer num; - String objective; - String gate; - Date started; - Integer durationInDays; - Date accomplished; - - Objective(Integer num, String objective, String gate, Date started, Integer durationInDays, Date accomplished) { - this.num = num; - this.objective = objective; - this.gate = gate; - this.started = started; - this.durationInDays = durationInDays; - this.accomplished = accomplished; - } - - public void setStarted(Date started) { - this.started = started; - } - - public boolean isStarted() { - return started.getTime() > 0; - } - - boolean isFinished() { - return accomplished.getTime() != 0; - } + private void setupObjectives() { + objectives.add(new Objective1()); + objectives.add(new Objective2()); + objectives.add(new Objective3()); + objectives.add(new Objective4()); + objectives.add(new Objective5()); + objectives.add(new Objective6()); + objectives.add(new Objective7()); + objectives.add(new Objective8()); } - // Objective 0 - public static boolean bgIsAvailableInNS = false; - public static boolean pumpStatusIsAvailableInNS = false; - // Objective 1 - public static Integer manualEnacts = 0; - private static final Integer manualEnactsNeeded = 20; - - class RequirementResult { - boolean done = false; - String comment = ""; - - RequirementResult(boolean done, String comment) { - this.done = done; - this.comment = comment; + public void reset() { + for (Objective objective : objectives) { + objective.setStartedOn(null); + objective.setAccomplishedOn(null); } - } - - private String yesOrNo(boolean yes) { - if (yes) return "☺"; - else return "---"; - } - - RequirementResult requirementsMet(Integer objNum) { - switch (objNum) { - case 0: - boolean isVirtualPump = VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); - boolean vpUploadEnabled = SP.getBoolean("virtualpump_uploadstatus", false); - boolean vpUploadNeeded = !isVirtualPump || vpUploadEnabled; - boolean hasBGData = DatabaseHelper.lastBg() != null; - - boolean apsEnabled = false; - APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS(); - if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS)) - apsEnabled = true; - - boolean profileSwitchExists = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null; - - return new RequirementResult(hasBGData && bgIsAvailableInNS && pumpStatusIsAvailableInNS && NSClientPlugin.getPlugin().hasWritePermission() && LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && apsEnabled && vpUploadNeeded && profileSwitchExists, - MainApp.gs(R.string.objectives_bgavailableinns) + ": " + yesOrNo(bgIsAvailableInNS) - + "\n" + MainApp.gs(R.string.nsclienthaswritepermission) + ": " + yesOrNo(NSClientPlugin.getPlugin().hasWritePermission()) - + (isVirtualPump ? "\n" + MainApp.gs(R.string.virtualpump_uploadstatus_title) + ": " + yesOrNo(vpUploadEnabled) : "") - + "\n" + MainApp.gs(R.string.objectives_pumpstatusavailableinns) + ": " + yesOrNo(pumpStatusIsAvailableInNS) - + "\n" + MainApp.gs(R.string.hasbgdata) + ": " + yesOrNo(hasBGData) - + "\n" + MainApp.gs(R.string.loopenabled) + ": " + yesOrNo(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) - + "\n" + MainApp.gs(R.string.apsselected) + ": " + yesOrNo(apsEnabled) - + "\n" + MainApp.gs(R.string.activate_profile) + ": " + yesOrNo(profileSwitchExists) - ); - case 1: - return new RequirementResult(manualEnacts >= manualEnactsNeeded, - MainApp.gs(R.string.objectives_manualenacts) + ": " + manualEnacts + "/" + manualEnactsNeeded); - case 2: - return new RequirementResult(true, ""); - case 3: - Constraint closedLoopEnabled = new Constraint<>(true); - SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled); - return new RequirementResult(closedLoopEnabled.value(), MainApp.gs(R.string.closedmodeenabled) + ": " + yesOrNo(closedLoopEnabled.value())); - case 4: - double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value(); - boolean maxIobSet = maxIOB > 0; - return new RequirementResult(maxIobSet, MainApp.gs(R.string.maxiobset) + ": " + yesOrNo(maxIobSet)); - default: - return new RequirementResult(true, ""); - } - } - - - void initializeData() { bgIsAvailableInNS = false; pumpStatusIsAvailableInNS = false; manualEnacts = 0; - - objectives = new ArrayList<>(); - objectives.add(new Objective(0, - MainApp.gs(R.string.objectives_0_objective), - MainApp.gs(R.string.objectives_0_gate), - new Date(0), - 0, // 0 day - new Date(0))); - objectives.add(new Objective(1, - MainApp.gs(R.string.objectives_1_objective), - MainApp.gs(R.string.objectives_1_gate), - new Date(0), - 7, // 7 days - new Date(0))); - objectives.add(new Objective(2, - MainApp.gs(R.string.objectives_2_objective), - MainApp.gs(R.string.objectives_2_gate), - new Date(0), - 0, // 0 days - new Date(0))); - objectives.add(new Objective(3, - MainApp.gs(R.string.objectives_3_objective), - MainApp.gs(R.string.objectives_3_gate), - new Date(0), - 5, // 5 days - new Date(0))); - objectives.add(new Objective(4, - MainApp.gs(R.string.objectives_4_objective), - MainApp.gs(R.string.objectives_4_gate), - new Date(0), - 1, - new Date(0))); - objectives.add(new Objective(5, - MainApp.gs(R.string.objectives_5_objective), - MainApp.gs(R.string.objectives_5_gate), - new Date(0), - 7, - new Date(0))); - objectives.add(new Objective(6, - MainApp.gs(R.string.objectives_6_objective), - "", - new Date(0), - 28, - new Date(0))); - objectives.add(new Objective(7, - MainApp.gs(R.string.objectives_7_objective), - "", - new Date(0), - 28, - new Date(0))); + saveProgress(); } public static void saveProgress() { - if (objectives != null) { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); - SharedPreferences.Editor editor = settings.edit(); - for (int num = 0; num < objectives.size(); num++) { - Objective o = objectives.get(num); - editor.putString("Objectives" + num + "started", Long.toString(o.started.getTime())); - editor.putString("Objectives" + num + "accomplished", Long.toString(o.accomplished.getTime())); - } - editor.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS); - editor.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS); - editor.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts)); - editor.apply(); - if (Config.logPrefsChange) - log.debug("Objectives stored"); - MainApp.bus().post(new EventObjectivesSaved()); - } + SP.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS); + SP.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS); + SP.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts)); + if (Config.logPrefsChange) + log.debug("Objectives stored"); + MainApp.bus().post(new EventObjectivesSaved()); } private void loadProgress() { - for (int num = 0; num < objectives.size(); num++) { - Objective o = objectives.get(num); - try { - o.started = new Date(SP.getLong("Objectives" + num + "started", 0L)); - o.accomplished = new Date(SP.getLong("Objectives" + num + "accomplished", 0L)); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - } bgIsAvailableInNS = SP.getBoolean("Objectives" + "bgIsAvailableInNS", false); pumpStatusIsAvailableInNS = SP.getBoolean("Objectives" + "pumpStatusIsAvailableInNS", false); try { @@ -260,11 +111,15 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface log.debug("Objectives loaded"); } + public static List getObjectives() { + return objectives; + } + /** * Constraints interface **/ @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!objectives.get(0).isStarted()) value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 1), this); return value; @@ -300,7 +155,7 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface @Override public Constraint applyMaxIOBConstraints(Constraint maxIob) { - if (objectives.get(3).isStarted() && !objectives.get(3).isFinished()) + if (objectives.get(3).isStarted() && !objectives.get(3).isAccomplished()) maxIob.set(0d, String.format(MainApp.gs(R.string.objectivenotfinished), 4), this); return maxIob; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java new file mode 100644 index 0000000000..bba29c5201 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java @@ -0,0 +1,140 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import android.support.annotation.StringRes; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.utils.SP; + +public abstract class Objective { + + private int number; + @StringRes + private int objective; + @StringRes + private int gate; + private Date startedOn; + private Date accomplishedOn; + private List tasks = new ArrayList<>(); + + public Objective(int number, @StringRes int objective, @StringRes int gate) { + this.number = number; + this.objective = objective; + this.gate = gate; + startedOn = new Date(SP.getLong("Objectives" + number + "started", 0L)); + if (startedOn.getTime() == 0L) startedOn = null; + accomplishedOn = new Date(SP.getLong("Objectives" + number + "accomplished", 0L)); + if (accomplishedOn.getTime() == 0L) accomplishedOn = null; + setupTasks(tasks); + for (Task task : tasks) task.objective = this; + } + + public boolean isCompleted() { + for (Task task : tasks) { + if (!task.shouldBeIgnored() && !task.isCompleted()) + return false; + } + return true; + } + + public boolean isAccomplished() { + return accomplishedOn != null; + } + + public boolean isStarted() { + return startedOn != null; + } + + public Date getStartedOn() { + return startedOn; + } + + public int getObjective() { + return objective; + } + + public int getGate() { + return gate; + } + + public void setStartedOn(Date startedOn) { + this.startedOn = startedOn; + SP.putLong("Objectives" + number + "started", startedOn == null ? 0 : startedOn.getTime()); + } + + public void setAccomplishedOn(Date accomplishedOn) { + this.accomplishedOn = accomplishedOn; + SP.putLong("Objectives" + number + "accomplished", accomplishedOn == null ? 0 : accomplishedOn.getTime()); + } + + protected void setupTasks(List tasks) { + + } + + public List getTasks() { + return tasks; + } + + public abstract class Task { + @StringRes + private int task; + private Objective objective; + + public Task(@StringRes int task) { + this.task = task; + } + + public int getTask() { + return task; + } + + protected Objective getObjective() { + return objective; + } + + public abstract boolean isCompleted(); + + public String getProgress() { + return MainApp.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet); + } + + public boolean shouldBeIgnored() { + return false; + } + } + + public class MinimumDurationTask extends Task { + + private long minimumDuration; + + public MinimumDurationTask(long minimumDuration) { + super(R.string.time_elapsed); + this.minimumDuration = minimumDuration; + } + + @Override + public boolean isCompleted() { + return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn().getTime() >= minimumDuration; + } + + @Override + public String getProgress() { + return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn().getTime()) + + " / " + getDurationText(minimumDuration); + } + + private String getDurationText(long duration) { + int days = (int) Math.floor((double) duration / (24D * 60D * 60D * 1000D)); + int hours = (int) Math.floor((double) duration / (60D * 60D * 1000D)); + int minutes = (int) Math.floor((double) duration / (60D * 1000D)); + if (days > 0) return MainApp.gq(R.plurals.objective_days, days, days); + else if (hours > 0) return MainApp.gq(R.plurals.objective_hours, hours, hours); + else return MainApp.gq(R.plurals.objective_minutes, minutes, minutes); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java new file mode 100644 index 0000000000..e374126b11 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java @@ -0,0 +1,84 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.DatabaseHelper; +import info.nightscout.androidaps.interfaces.APSInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.Loop.LoopPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.SP; + +public class Objective1 extends Objective { + + public Objective1() { + super(0, R.string.objectives_0_objective, R.string.objectives_0_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new Task(R.string.objectives_bgavailableinns) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.bgIsAvailableInNS; + } + }); + tasks.add(new Task(R.string.nsclienthaswritepermission) { + @Override + public boolean isCompleted() { + return NSClientPlugin.getPlugin().hasWritePermission(); + } + }); + tasks.add(new Task(R.string.virtualpump_uploadstatus_title) { + @Override + public boolean isCompleted() { + return SP.getBoolean("virtualpump_uploadstatus", false); + } + + @Override + public boolean shouldBeIgnored() { + return !VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); + } + }); + tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.pumpStatusIsAvailableInNS; + } + }); + tasks.add(new Task(R.string.hasbgdata) { + @Override + public boolean isCompleted() { + return DatabaseHelper.lastBg() != null; + } + }); + tasks.add(new Task(R.string.loopenabled) { + @Override + public boolean isCompleted() { + return LoopPlugin.getPlugin().isEnabled(PluginType.LOOP); + } + }); + tasks.add(new Task(R.string.apsselected) { + @Override + public boolean isCompleted() { + APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS(); + if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS)) + return true; + return false; + } + }); + tasks.add(new Task(R.string.activate_profile) { + @Override + public boolean isCompleted() { + return TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java new file mode 100644 index 0000000000..305f00ec6f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; + +public class Objective2 extends Objective { + + public static final int MANUAL_ENACTS_NEEDED = 20; + + public Objective2() { + super(1, R.string.objectives_1_objective, R.string.objectives_1_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.objectives_manualenacts) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED; + } + + @Override + public String getProgress() { + if (ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED) return MainApp.gs(R.string.completed_well_done); + else return ObjectivesPlugin.manualEnacts + " / " + MANUAL_ENACTS_NEEDED; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java new file mode 100644 index 0000000000..8a84e3c83c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import info.nightscout.androidaps.R; + +public class Objective3 extends Objective { + + public Objective3() { + super(2, R.string.objectives_2_objective, R.string.objectives_2_gate); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java new file mode 100644 index 0000000000..7700c94cb6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; + +public class Objective4 extends Objective { + + public Objective4() { + super(3, R.string.objectives_3_objective, R.string.objectives_4_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(5L * 24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.closedmodeenabled) { + @Override + public boolean isCompleted() { + Constraint closedLoopEnabled = new Constraint<>(true); + SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled); + return closedLoopEnabled.value(); + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java new file mode 100644 index 0000000000..563a114e05 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +public class Objective5 extends Objective { + + public Objective5() { + super(4, R.string.objectives_4_objective, R.string.objectives_4_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.maxiobset) { + @Override + public boolean isCompleted() { + double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value(); + return maxIOB > 0; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java new file mode 100644 index 0000000000..7106d7522b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective6 extends Objective { + + public Objective6() { + super(5, R.string.objectives_5_objective, R.string.objectives_5_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java new file mode 100644 index 0000000000..cfd041bf13 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective7 extends Objective { + + public Objective7() { + super(6, R.string.objectives_6_objective, 0); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java new file mode 100644 index 0000000000..70d75fd900 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective8 extends Objective { + + public Objective8() { + super(7, R.string.objectives_7_objective, 0); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java index 6a9613e92a..3d17970edc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java @@ -49,7 +49,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { * Constraints interface **/ @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable) value.set(false, MainApp.gs(R.string.pumpisnottempbasalcapable), this); return value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 79ab63e2ac..c826b70437 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -16,8 +16,8 @@ import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.plugins.Overview.graphExtensions.Scale; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.utils.SP; @@ -56,11 +56,11 @@ public class AutosensData implements DataPointWithLabelInterface { } public long time = 0L; - long chartTime; + public double bg = 0; // mgdl + private long chartTime; public String pastSensitivity = ""; public double deviation = 0d; - boolean nonCarbsDeviation = false; - public boolean nonEqualDeviation = false; + public boolean validDeviation = false; List activeCarbsList = new ArrayList<>(); double absorbed = 0d; public double carbsFromBolus = 0d; @@ -76,6 +76,14 @@ public class AutosensData implements DataPointWithLabelInterface { public double usedMinCarbsImpact = 0d; public boolean failoverToMinAbsorbtionRate = false; + // Oref1 + public boolean absorbing = false; + public double mealCarbs = 0; + public int mealStartCounter = 999; + public String type = ""; + public boolean uam = false; + public List extraDeviation = new ArrayList<>(); + @Override public String toString() { return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation =" + slopeFromMinDeviation; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java index 4e0f8ef107..6602038fb4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java @@ -32,4 +32,8 @@ public class AutosensResult { return ret; } + @Override + public String toString() { + return json().toString(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 2e8e16a226..d6fc244eef 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -35,6 +35,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; @@ -68,7 +69,7 @@ public class IobCobCalculatorPlugin extends PluginBase { final Object dataLock = new Object(); boolean stopCalculationTrigger = false; - private IobCobThread thread = null; + private Thread thread = null; public IobCobCalculatorPlugin() { super(new PluginDescription() @@ -387,10 +388,6 @@ public class IobCobCalculatorPlugin extends PluginBase { //log.debug(">>> getAutosensData Cache hit " + data.log(time)); return data; } else { - if (time > now) { - // data may not be calculated yet, use last data - return getLastAutosensData("getAutosensData"); - } //log.debug(">>> getAutosensData Cache miss " + new Date(time).toLocaleString()); return null; } @@ -541,7 +538,10 @@ public class IobCobCalculatorPlugin extends PluginBase { public void runCalculation(String from, long start, boolean bgDataReload, Event cause) { log.debug("Starting calculation thread: " + from); if (thread == null || thread.getState() == Thread.State.TERMINATED) { - thread = new IobCobThread(this, from, start, bgDataReload, cause); + if (SensitivityOref1Plugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) + thread = new IobCobOref1Thread(this, from, start, bgDataReload, cause); + else + thread = new IobCobThread(this, from, start, bgDataReload, cause); thread.start(); } } @@ -580,7 +580,9 @@ public class IobCobCalculatorPlugin extends PluginBase { ev.isChanged(R.string.key_age) || ev.isChanged(R.string.key_absorption_maxtime) || ev.isChanged(R.string.key_openapsama_min_5m_carbimpact) || - ev.isChanged(R.string.key_absorption_cutoff) + ev.isChanged(R.string.key_absorption_cutoff) || + ev.isChanged(R.string.key_openapsama_autosens_max) || + ev.isChanged(R.string.key_openapsama_autosens_min) ) { stopCalculation("onEventPreferenceChange"); synchronized (dataLock) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java new file mode 100644 index 0000000000..725cfab741 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java @@ -0,0 +1,354 @@ +package info.nightscout.androidaps.plugins.IobCobCalculator; + +import android.content.Context; +import android.os.PowerManager; +import android.support.v4.util.LongSparseArray; + +import com.crashlytics.android.answers.CustomEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +import info.nightscout.androidaps.BuildConfig; +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.IobTotal; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.events.Event; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; +import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; +import info.nightscout.androidaps.plugins.Treatments.Treatment; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.FabricPrivacy; +import info.nightscout.utils.SP; + +import static info.nightscout.utils.DateUtil.now; +import static java.util.Calendar.MINUTE; + +/** + * Created by mike on 23.01.2018. + */ + +public class IobCobOref1Thread extends Thread { + private static Logger log = LoggerFactory.getLogger(IobCobOref1Thread.class); + private final Event cause; + + private IobCobCalculatorPlugin iobCobCalculatorPlugin; + private boolean bgDataReload; + private String from; + private long start; + + private PowerManager.WakeLock mWakeLock; + + public IobCobOref1Thread(IobCobCalculatorPlugin plugin, String from, long start, boolean bgDataReload, Event cause) { + super(); + + this.iobCobCalculatorPlugin = plugin; + this.bgDataReload = bgDataReload; + this.from = from; + this.cause = cause; + this.start = start; + + PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "iobCobThread"); + } + + @Override + public final void run() { + mWakeLock.acquire(); + try { + if (MainApp.getConfigBuilder() == null) { + log.debug("Aborting calculation thread (ConfigBuilder not ready): " + from); + return; // app still initializing + } + if (!MainApp.getConfigBuilder().isProfileValid("IobCobThread")) { + log.debug("Aborting calculation thread (No profile): " + from); + return; // app still initializing + } + //log.debug("Locking calculateSensitivityData"); + + long oldestTimeWithData = iobCobCalculatorPlugin.oldestDataAvailable(); + + synchronized (iobCobCalculatorPlugin.dataLock) { + if (bgDataReload) { + iobCobCalculatorPlugin.loadBgData(start); + iobCobCalculatorPlugin.createBucketedData(); + } + List bucketed_data = iobCobCalculatorPlugin.getBucketedData(); + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); + + if (bucketed_data == null || bucketed_data.size() < 3) { + log.debug("Aborting calculation thread (No bucketed data available): " + from); + return; + } + + long prevDataTime = IobCobCalculatorPlugin.roundUpTime(bucketed_data.get(bucketed_data.size() - 3).date); + log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString()); + AutosensData previous = autosensDataTable.get(prevDataTime); + // start from oldest to be able sub cob + for (int i = bucketed_data.size() - 4; i >= 0; i--) { + String progress = i + (MainApp.isDev() ? " (" + from + ")" : ""); + MainApp.bus().post(new EventIobCalculationProgress(progress)); + + if (iobCobCalculatorPlugin.stopCalculationTrigger) { + iobCobCalculatorPlugin.stopCalculationTrigger = false; + log.debug("Aborting calculation thread (trigger): " + from); + return; + } + // check if data already exists + long bgTime = bucketed_data.get(i).date; + bgTime = IobCobCalculatorPlugin.roundUpTime(bgTime); + if (bgTime > IobCobCalculatorPlugin.roundUpTime(now())) + continue; + + AutosensData existing; + if ((existing = autosensDataTable.get(bgTime)) != null) { + previous = existing; + continue; + } + + Profile profile = MainApp.getConfigBuilder().getProfile(bgTime); + if (profile == null) { + log.debug("Aborting calculation thread (no profile): " + from); + return; // profile not set yet + } + + if (Config.logAutosensData) + log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); + + double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); + + AutosensData autosensData = new AutosensData(); + autosensData.time = bgTime; + if (previous != null) + autosensData.activeCarbsList = new ArrayList<>(previous.activeCarbsList); + else + autosensData.activeCarbsList = new ArrayList<>(); + + //console.error(bgTime , bucketed_data[i].glucose); + double bg; + double avgDelta; + double delta; + bg = bucketed_data.get(i).value; + if (bg < 39 || bucketed_data.get(i + 3).value < 39) { + log.error("! value < 39"); + continue; + } + autosensData.bg = bg; + delta = (bg - bucketed_data.get(i + 1).value); + avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; + + IobTotal iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile); + + double bgi = -iob.activity * sens * 5; + double deviation = delta - bgi; + double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; + + double slopeFromMaxDeviation = 0; + double slopeFromMinDeviation = 999; + double maxDeviation = 0; + double minDeviation = 999; + + // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope + long hourago = bgTime + 10 * 1000 - 60 * 60 * 1000L; + AutosensData hourAgoData = IobCobCalculatorPlugin.getPlugin().getAutosensData(hourago); + if (hourAgoData != null) { + int initialIndex = autosensDataTable.indexOfKey(hourAgoData.time); + if (Config.logAutosensData) + log.debug(">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + "hourAgoData=" + hourAgoData.toString()); + int past = 1; + try { + for (; past < 12; past++) { + AutosensData ad = autosensDataTable.valueAt(initialIndex + past); + double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; + if (ad.avgDeviation > maxDeviation) { + slopeFromMaxDeviation = Math.min(0, deviationSlope); + maxDeviation = ad.avgDeviation; + } + if (ad.avgDeviation < minDeviation) { + slopeFromMinDeviation = Math.max(0, deviationSlope); + minDeviation = ad.avgDeviation; + } + + //if (Config.logAutosensData) + // log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation=" + slopeFromMinDeviation); + } + } catch (Exception e) { + log.error("Unhandled exception", e); + FabricPrivacy.logException(e); + FabricPrivacy.getInstance().logCustom(new CustomEvent("CatchedError") + .putCustomAttribute("buildversion", BuildConfig.BUILDVERSION) + .putCustomAttribute("version", BuildConfig.VERSION) + .putCustomAttribute("autosensDataTable", iobCobCalculatorPlugin.getAutosensDataTable().toString()) + .putCustomAttribute("for_data", ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + "hourAgoData=" + hourAgoData.toString()) + .putCustomAttribute("past", past) + ); + } + } + } + + List recentTreatments = TreatmentsPlugin.getPlugin().getTreatments5MinBackFromHistory(bgTime); + for (int ir = 0; ir < recentTreatments.size(); ir++) { + autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; + autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); + } + + + // if we are absorbing carbs + if (previous != null && previous.cob > 0) { + // calculate sum of min carb impact from all active treatments + double totalMinCarbsImpact = 0d; +// if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) { + //when the impact depends on a max time, sum them up as smaller carb sizes make them smaller +// for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) { +// AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii); +// totalMinCarbsImpact += c.min5minCarbImpact; +// } +// } else { + //Oref sensitivity + totalMinCarbsImpact = SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact); +// } + + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption per active treatment + double ci = Math.max(deviation, totalMinCarbsImpact); + if (ci != deviation) + autosensData.failoverToMinAbsorbtionRate = true; + autosensData.absorbed = ci * profile.getIc(bgTime) / sens; + // and add that to the running total carbsAbsorbed + autosensData.cob = Math.max(previous.cob - autosensData.absorbed, 0d); + autosensData.mealCarbs = previous.mealCarbs; + autosensData.substractAbosorbedCarbs(); + autosensData.usedMinCarbsImpact = totalMinCarbsImpact; + autosensData.absorbing = previous.absorbing; + autosensData.mealStartCounter = previous.mealStartCounter; + autosensData.type = previous.type; + autosensData.uam = previous.uam; + } + + autosensData.removeOldCarbs(bgTime); + autosensData.cob += autosensData.carbsFromBolus; + autosensData.mealCarbs += autosensData.carbsFromBolus; + autosensData.deviation = deviation; + autosensData.bgi = bgi; + autosensData.delta = delta; + autosensData.avgDelta = avgDelta; + autosensData.avgDeviation = avgDeviation; + autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation; + autosensData.slopeFromMinDeviation = slopeFromMinDeviation; + + + // If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens + if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) { + if (deviation > 0) + autosensData.absorbing = true; + else + autosensData.absorbing = false; + // stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h + if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) { + autosensData.absorbing = false; + } + if (!autosensData.absorbing && autosensData.cob < 0.5) { + autosensData.mealCarbs = 0; + } + // check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag + if (!autosensData.type.equals("csf")) { +// process.stderr.write("("); + autosensData.mealStartCounter = 0; + } + autosensData.mealStartCounter++; + autosensData.type = "csf"; + } else { + // check previous "type" value, and if it was csf, set a mealAbsorption end flag + if (autosensData.type.equals("csf")) { +// process.stderr.write(")"); + } + + double currentBasal = profile.getBasal(bgTime); + // always exclude the first 45m after each carb entry + //if (iob.iob > currentBasal || uam ) { + if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) { + autosensData.mealStartCounter++; + if (deviation > 0) + autosensData.uam = true; + else + autosensData.uam = false; + if (!autosensData.type.equals("uam")) { +// process.stderr.write("u("); + } + autosensData.type = "uam"; + } else { + if (autosensData.type.equals("uam")) { +// process.stderr.write(")"); + } + autosensData.type = "non-meal"; + } + } + + // Exclude meal-related deviations (carb absorption) from autosens + if (autosensData.type.equals("non-meal")) { + if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { + autosensData.pastSensitivity = "="; + autosensData.validDeviation = true; + } else if (deviation > 0) { + autosensData.pastSensitivity = "+"; + autosensData.validDeviation = true; + } else { + autosensData.pastSensitivity = "-"; + autosensData.validDeviation = true; + } + } else if (autosensData.type.equals("uam")) { + autosensData.pastSensitivity = "u"; + } else { + autosensData.pastSensitivity = "x"; + } + //log.debug("TIME: " + new Date(bgTime).toString() + " BG: " + bg + " SENS: " + sens + " DELTA: " + delta + " AVGDELTA: " + avgDelta + " IOB: " + iob.iob + " ACTIVITY: " + iob.activity + " BGI: " + bgi + " DEVIATION: " + deviation); + + // add an extra negative deviation if a high temptarget is running and exercise mode is set + if (SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) { + TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(bgTime); + if (tempTarget != null && tempTarget.target() >= 100) { + autosensData.extraDeviation.add(-(tempTarget.target() - 100) / 20); + } + } + + // add one neutral deviation every 2 hours to help decay over long exclusion periods + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(bgTime); + int min = calendar.get(MINUTE); + int hours = calendar.get(Calendar.HOUR_OF_DAY); + if (min >= 0 && min < 5 && hours % 2 == 0) + autosensData.extraDeviation.add(0d); + + previous = autosensData; + if (bgTime < now()) + autosensDataTable.put(bgTime, autosensData); + if (Config.logAutosensData) + log.debug("Running detectSensitivity from: " + DateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + DateUtil.dateAndTimeString(bgTime)); + autosensData.autosensRatio = iobCobCalculatorPlugin.detectSensitivity(oldestTimeWithData, bgTime).ratio; + if (Config.logAutosensData) + log.debug(autosensData.toString()); + } + } + MainApp.bus().post(new EventAutosensCalculationFinished(cause)); + log.debug("Finishing calculation thread: " + from); + } finally { + mWakeLock.release(); + MainApp.bus().post(new EventIobCalculationProgress("")); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java index 9818ed17e4..2ba476996f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java @@ -26,8 +26,8 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; @@ -145,6 +145,7 @@ public class IobCobThread extends Thread { log.error("! value < 39"); continue; } + autosensData.bg = bg; delta = (bg - bucketed_data.get(i + 1).value); avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; @@ -245,26 +246,29 @@ public class IobCobThread extends Thread { // calculate autosens only without COB if (autosensData.cob <= 0) { if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { - autosensData.pastSensitivity += "="; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "="; + autosensData.validDeviation = true; } else if (deviation > 0) { - autosensData.pastSensitivity += "+"; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "+"; + autosensData.validDeviation = true; } else { - autosensData.pastSensitivity += "-"; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "-"; + autosensData.validDeviation = true; } - autosensData.nonCarbsDeviation = true; } else { - autosensData.pastSensitivity += "C"; + autosensData.pastSensitivity = "C"; } //log.debug("TIME: " + new Date(bgTime).toString() + " BG: " + bg + " SENS: " + sens + " DELTA: " + delta + " AVGDELTA: " + avgDelta + " IOB: " + iob.iob + " ACTIVITY: " + iob.activity + " BGI: " + bgi + " DEVIATION: " + deviation); previous = autosensData; - autosensDataTable.put(bgTime, autosensData); + if (bgTime < now()) + autosensDataTable.put(bgTime, autosensData); if (Config.logAutosensData) log.debug("Running detectSensitivity from: " + DateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + DateUtil.dateAndTimeString(bgTime)); - autosensData.autosensRatio = iobCobCalculatorPlugin.detectSensitivity(oldestTimeWithData, bgTime).ratio; + AutosensResult sensitivity = iobCobCalculatorPlugin.detectSensitivity(oldestTimeWithData, bgTime); + if (Config.logAutosensData) + log.debug("Sensitivity result: " + sensitivity.toString()); + autosensData.autosensRatio = sensitivity.ratio; if (Config.logAutosensData) log.debug(autosensData.toString()); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 87e55c7dc6..ce63ad67e5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -10,7 +10,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Date; import java.util.List; import info.nightscout.androidaps.MainApp; @@ -28,7 +27,7 @@ import info.nightscout.utils.DecimalFormatter; public class APSResult { private static Logger log = LoggerFactory.getLogger(APSResult.class); - public Date date; + public long date = 0; public String reason; public double rate; public int duration; @@ -133,8 +132,8 @@ public class APSResult { public List getPredictions() { List array = new ArrayList<>(); try { - long startTime = date.getTime(); - if (json.has("predBGs")) { + long startTime = date; + if (json != null && json.has("predBGs")) { JSONObject predBGs = json.getJSONObject("predBGs"); if (predBGs.has("IOB")) { JSONArray iob = predBGs.getJSONArray("IOB"); @@ -196,8 +195,8 @@ public class APSResult { public long getLatestPredictionsTime() { long latest = 0; try { - long startTime = date != null ? date.getTime() : 0; - if (json.has("predBGs")) { + long startTime = date; + if (json != null && json.has("predBGs")) { JSONObject predBGs = json.getJSONObject("predBGs"); if (predBGs.has("IOB")) { JSONArray iob = predBGs.getJSONArray("IOB"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java index 4ad35c0d02..7fa810f8c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java @@ -80,7 +80,7 @@ public class LoopFragment extends SubscriberFragment { clearGUI(); final Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(() -> lastRunView.setText(ev.text)); + activity.runOnUiThread(() -> { synchronized (LoopFragment.this) { if (lastRunView != null) lastRunView.setText(ev.text); } }); } @@ -89,26 +89,29 @@ public class LoopFragment extends SubscriberFragment { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - LoopPlugin.LastRun lastRun = LoopPlugin.lastRun; - if (lastRun != null) { - requestView.setText(lastRun.request != null ? lastRun.request.toSpanned() : ""); - constraintsProcessedView.setText(lastRun.constraintsProcessed != null ? lastRun.constraintsProcessed.toSpanned() : ""); - sourceView.setText(lastRun.source != null ? lastRun.source : ""); - lastRunView.setText(lastRun.lastAPSRun != null && lastRun.lastAPSRun.getTime() != 0 ? lastRun.lastAPSRun.toLocaleString() : ""); - lastEnactView.setText(lastRun.lastEnact != null && lastRun.lastEnact.getTime() != 0 ? lastRun.lastEnact.toLocaleString() : ""); - tbrSetByPumpView.setText(lastRun.tbrSetByPump != null ? Html.fromHtml(lastRun.tbrSetByPump.toHtml()) : ""); - smbSetByPumpView.setText(lastRun.smbSetByPump != null ? Html.fromHtml(lastRun.smbSetByPump.toHtml()) : ""); + synchronized (LoopFragment.this) { + if (!isBound()) return; + LoopPlugin.LastRun lastRun = LoopPlugin.lastRun; + if (lastRun != null) { + requestView.setText(lastRun.request != null ? lastRun.request.toSpanned() : ""); + constraintsProcessedView.setText(lastRun.constraintsProcessed != null ? lastRun.constraintsProcessed.toSpanned() : ""); + sourceView.setText(lastRun.source != null ? lastRun.source : ""); + lastRunView.setText(lastRun.lastAPSRun != null && lastRun.lastAPSRun.getTime() != 0 ? lastRun.lastAPSRun.toLocaleString() : ""); + lastEnactView.setText(lastRun.lastEnact != null && lastRun.lastEnact.getTime() != 0 ? lastRun.lastEnact.toLocaleString() : ""); + tbrSetByPumpView.setText(lastRun.tbrSetByPump != null ? Html.fromHtml(lastRun.tbrSetByPump.toHtml()) : ""); + smbSetByPumpView.setText(lastRun.smbSetByPump != null ? Html.fromHtml(lastRun.smbSetByPump.toHtml()) : ""); - String constraints = ""; - if (lastRun.constraintsProcessed != null) { - Constraint allConstraints = new Constraint<>(0d); - if (lastRun.constraintsProcessed.rateConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.rateConstraint); - if (lastRun.constraintsProcessed.smbConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.smbConstraint); - constraints = allConstraints.getMostLimitedReasons(); + String constraints = ""; + if (lastRun.constraintsProcessed != null) { + Constraint allConstraints = new Constraint<>(0d); + if (lastRun.constraintsProcessed.rateConstraint != null) + allConstraints.copyReasons(lastRun.constraintsProcessed.rateConstraint); + if (lastRun.constraintsProcessed.smbConstraint != null) + allConstraints.copyReasons(lastRun.constraintsProcessed.smbConstraint); + constraints = allConstraints.getMostLimitedReasons(); + } + constraintsView.setText(constraints); } - constraintsView.setText(constraints); } }); } @@ -117,13 +120,29 @@ public class LoopFragment extends SubscriberFragment { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - requestView.setText(""); - constraintsProcessedView.setText(""); - sourceView.setText(""); - lastRunView.setText(""); - lastEnactView.setText(""); - tbrSetByPumpView.setText(""); - smbSetByPumpView.setText(""); + synchronized (LoopFragment.this) { + if (isBound()) { + requestView.setText(""); + constraintsProcessedView.setText(""); + sourceView.setText(""); + lastRunView.setText(""); + lastEnactView.setText(""); + tbrSetByPumpView.setText(""); + smbSetByPumpView.setText(""); + } + } }); } + + boolean isBound() { + return requestView != null + && constraintsProcessedView != null + && sourceView != null + && lastRunView != null + && lastEnactView != null + && tbrSetByPumpView != null + && smbSetByPumpView != null + && constraintsView != null + && runNowButton != null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index c01cf0a641..480646ccfc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -46,6 +46,7 @@ import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -332,7 +333,9 @@ public class LoopPlugin extends PluginBase { Constraint closedLoopEnabled = MainApp.getConstraintChecker().isClosedLoopAllowed(); if (closedLoopEnabled.value()) { - if (result.isChangeRequested()) { + if (result.isChangeRequested() + && !ConfigBuilderPlugin.getCommandQueue().bolusInQueue() + && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BOLUS)) { final PumpEnactResult waiting = new PumpEnactResult(); waiting.queued = true; if (resultAfterConstraints.tempBasalRequested) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java index 04b587ca24..ba6ec2c80b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java @@ -122,7 +122,8 @@ class NsClientReceiverDelegate { boolean newAllowedState = true; if (ev.wifiConnected) { - if (!allowedSSIDs.trim().isEmpty() && !allowedSSIDs.contains(ev.ssid)) { + if (!allowedSSIDs.trim().isEmpty() && + (!allowedSSIDs.contains(ev.getSsid()) && !allowedSSIDs.contains(ev.ssid))) { newAllowedState = false; } } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java index d1755dd4c1..29d1b33467 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java @@ -13,6 +13,7 @@ import java.util.Iterator; import java.util.Map; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.utils.DateUtil; import info.nightscout.utils.Round; import info.nightscout.utils.SP; @@ -374,7 +375,7 @@ public class NSDeviceStatus { public String getUploaderStatus() { Iterator iter = uploaders.entrySet().iterator(); int minBattery = 100; - while(iter.hasNext()) { + while (iter.hasNext()) { Map.Entry pair = (Map.Entry) iter.next(); Uploader uploader = (Uploader) pair.getValue(); if (minBattery > uploader.battery) @@ -388,7 +389,7 @@ public class NSDeviceStatus { StringBuilder string = new StringBuilder(); Iterator iter = uploaders.entrySet().iterator(); - while(iter.hasNext()) { + while (iter.hasNext()) { Map.Entry pair = (Map.Entry) iter.next(); Uploader uploader = (Uploader) pair.getValue(); String device = (String) pair.getKey(); @@ -398,4 +399,11 @@ public class NSDeviceStatus { return Html.fromHtml(string.toString()); } + public static APSResult getAPSResult() { + APSResult result = new APSResult(); + result.json = deviceStatusOpenAPSData.suggested; + result.date = deviceStatusOpenAPSData.clockSuggested; + return result; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java index cb2b4a0091..ac7a7c2b49 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -6,19 +6,18 @@ import org.mozilla.javascript.NativeObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.utils.DateUtil; public class DetermineBasalResultAMA extends APSResult { private static Logger log = LoggerFactory.getLogger(DetermineBasalResultAMA.class); - public double eventualBG; - public double snoozeBG; + private double eventualBG; + private double snoozeBG; - public DetermineBasalResultAMA(NativeObject result, JSONObject j) { + DetermineBasalResultAMA(NativeObject result, JSONObject j) { this(); - date = new Date(); + date = DateUtil.now(); json = j; if (result.containsKey("error")) { reason = result.get("error").toString(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 396338efa4..48a4c16ccd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -234,8 +234,8 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); - mProfile.put("high_temptarget_raises_sensitivity", SMBDefaults.high_temptarget_raises_sensitivity); - mProfile.put("low_temptarget_lowers_sensitivity", SMBDefaults.low_temptarget_lowers_sensitivity); + mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); + mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); mProfile.put("sensitivity_raises_target", SMBDefaults.sensitivity_raises_target); mProfile.put("resistance_lowers_target", SMBDefaults.resistance_lowers_target); mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java index 59c1dd95e3..d5f108bf4f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -5,22 +5,20 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.utils.DateUtil; public class DetermineBasalResultSMB extends APSResult { private static final Logger log = LoggerFactory.getLogger(DetermineBasalResultSMB.class); - public double eventualBG; - public double snoozeBG; - public double insulinReq; - public double carbsReq; + private double eventualBG; + private double snoozeBG; + //public double insulinReq; + //public double carbsReq; - public DetermineBasalResultSMB(JSONObject result) { + DetermineBasalResultSMB(JSONObject result) { this(); - date = new Date(); + date = DateUtil.now(); json = result; try { if (result.has("error")) { @@ -31,8 +29,8 @@ public class DetermineBasalResultSMB extends APSResult { reason = result.getString("reason"); if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG"); if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); - if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); - if (result.has("carbsReq")) carbsReq = result.getDouble("carbsReq"); + //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); + //if (result.has("carbsReq")) carbsReq = result.getDouble("carbsReq"); if (result.has("rate") && result.has("duration")) { tempBasalRequested = true; @@ -64,7 +62,7 @@ public class DetermineBasalResultSMB extends APSResult { } } - public DetermineBasalResultSMB() { + private DetermineBasalResultSMB() { hasPredictions = true; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java index 724bfcf86c..20ccd46b40 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java @@ -87,34 +87,37 @@ public class OpenAPSSMBFragment extends SubscriberFragment { activity.runOnUiThread(new Runnable() { @Override public void run() { - OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); - DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; - if (lastAPSResult != null) { - resultView.setText(JSONFormatter.format(lastAPSResult.json)); - requestView.setText(lastAPSResult.toSpanned()); - } - DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; - if (determineBasalAdapterSMBJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()).toString().trim()); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()).toString().trim()); - try { - JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); - iobDataView.setText((String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))).trim()); - } catch (JSONException e) { - log.error("Unhandled exception", e); - iobDataView.setText("JSONException see log for details"); + synchronized (OpenAPSSMBFragment.this) { + if (!isBound()) return; + OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); + DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; + if (lastAPSResult != null) { + resultView.setText(JSONFormatter.format(lastAPSResult.json)); + requestView.setText(lastAPSResult.toSpanned()); + } + DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; + if (determineBasalAdapterSMBJS != null) { + glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()).toString().trim()); + currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()).toString().trim()); + try { + JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); + iobDataView.setText((String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))).trim()); + } catch (JSONException e) { + log.error("Unhandled exception", e); + iobDataView.setText("JSONException see log for details"); + } + profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()).toString().trim()); + mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()).toString().trim()); + scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug().trim()); + if (lastAPSResult != null && lastAPSResult.inputConstraints != null) + constraintsView.setText(lastAPSResult.inputConstraints.getReasons().trim()); + } + if (plugin.lastAPSRun != null) { + lastRunView.setText(plugin.lastAPSRun.toLocaleString().trim()); + } + if (plugin.lastAutosensResult != null) { + autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json()).toString().trim()); } - profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()).toString().trim()); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()).toString().trim()); - scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug().trim()); - if (lastAPSResult != null && lastAPSResult.inputConstraints != null) - constraintsView.setText(lastAPSResult.inputConstraints.getReasons().trim()); - } - if (plugin.lastAPSRun != null) { - lastRunView.setText(plugin.lastAPSRun.toLocaleString().trim()); - } - if (plugin.lastAutosensResult != null) { - autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json()).toString().trim()); } } }); @@ -126,17 +129,36 @@ public class OpenAPSSMBFragment extends SubscriberFragment { activity.runOnUiThread(new Runnable() { @Override public void run() { - resultView.setText(text); - glucoseStatusView.setText(""); - currentTempView.setText(""); - iobDataView.setText(""); - profileView.setText(""); - mealDataView.setText(""); - autosensDataView.setText(""); - scriptdebugView.setText(""); - requestView.setText(""); - lastRunView.setText(""); + synchronized (OpenAPSSMBFragment.this) { + if (isBound()) { + resultView.setText(text); + glucoseStatusView.setText(""); + currentTempView.setText(""); + iobDataView.setText(""); + profileView.setText(""); + mealDataView.setText(""); + autosensDataView.setText(""); + scriptdebugView.setText(""); + requestView.setText(""); + lastRunView.setText(""); + } + } } }); } + + private boolean isBound() { + return run != null + && lastRunView != null + && constraintsView != null + && glucoseStatusView != null + && currentTempView != null + && iobDataView != null + && profileView != null + && mealDataView != null + && autosensDataView != null + && resultView != null + && scriptdebugView != null + && requestView != null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index e89a040316..2e8dea0662 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -25,14 +25,11 @@ import android.text.style.ForegroundColorSpan; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.ContextMenu; -import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; @@ -89,10 +86,12 @@ import info.nightscout.androidaps.plugins.Careportal.Dialogs.NewNSTreatmentDialo import info.nightscout.androidaps.plugins.Careportal.OptionsToShow; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.CobInfo; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; +import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; @@ -104,7 +103,6 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.NewInsulinDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; import info.nightscout.androidaps.plugins.Overview.activities.QuickWizardListActivity; -import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.plugins.Overview.graphData.GraphData; import info.nightscout.androidaps.plugins.Overview.notifications.NotificationRecyclerViewAdapter; import info.nightscout.androidaps.plugins.Overview.notifications.NotificationStore; @@ -116,6 +114,7 @@ import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; +import info.nightscout.utils.DefaultValueHelper; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.OKDialog; @@ -124,14 +123,19 @@ import info.nightscout.utils.SP; import info.nightscout.utils.SingleClickButton; import info.nightscout.utils.ToastUtils; +import static info.nightscout.utils.DateUtil.now; + public class OverviewFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { private static Logger log = LoggerFactory.getLogger(OverviewFragment.class); TextView timeView; TextView bgView; TextView arrowView; + TextView sensitivityView; TextView timeAgoView; + TextView timeAgoShortView; TextView deltaView; + TextView deltaShortView; TextView avgdeltaView; TextView baseBasalView; TextView extendedBolusView; @@ -177,8 +181,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, SingleClickButton cgmButton; SingleClickButton quickWizardButton; - CheckBox lockScreen; - boolean smallWidth; boolean smallHeight; @@ -235,8 +237,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (smallWidth) { arrowView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 35); } + sensitivityView = (TextView) view.findViewById(R.id.overview_sensitivity); timeAgoView = (TextView) view.findViewById(R.id.overview_timeago); + timeAgoShortView = (TextView) view.findViewById(R.id.overview_timeagoshort); deltaView = (TextView) view.findViewById(R.id.overview_delta); + deltaShortView = (TextView) view.findViewById(R.id.overview_deltashort); avgdeltaView = (TextView) view.findViewById(R.id.overview_avgdelta); baseBasalView = (TextView) view.findViewById(R.id.overview_basebasal); extendedBolusView = (TextView) view.findViewById(R.id.overview_extendedbolus); @@ -323,7 +328,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, iobGraph.getGridLabelRenderer().setHorizontalLabelsVisible(false); bgGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); iobGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); - iobGraph.getGridLabelRenderer().setNumVerticalLabels(5); + iobGraph.getGridLabelRenderer().setNumVerticalLabels(3); rangeToDisplay = SP.getInt(R.string.key_rangetodisplay, 6); @@ -340,18 +345,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, setupChartMenu(view); - lockScreen = (CheckBox) view.findViewById(R.id.overview_lockscreen); - if (lockScreen != null) { - lockScreen.setChecked(SP.getBoolean("lockscreen", false)); - lockScreen.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - SP.putBoolean("lockscreen", isChecked); - MainApp.bus().post(new EventSetWakeLock(isChecked)); - } - }); - } - return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -367,7 +360,13 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, @Override public void onClick(View v) { final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; - final boolean predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + boolean predictionsAvailable; + if (Config.APS) + predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + else if (Config.NSCLIENT) + predictionsAvailable = true; + else + predictionsAvailable = false; MenuItem item; CharSequence title; @@ -504,6 +503,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() != null) { menu.add(MainApp.gs(R.string.careportal_profileswitch)); } + } else if (v == tempTargetView) { + menu.setHeaderTitle(MainApp.gs(R.string.careportal_temporarytarget)); + menu.add(MainApp.gs(R.string.custom)); + menu.add(MainApp.gs(R.string.eatingsoon)); + menu.add(MainApp.gs(R.string.activity)); + menu.add(MainApp.gs(R.string.hypo)); + if (TreatmentsPlugin.getPlugin().getTempTargetFromHistory() != null) { + menu.add(MainApp.gs(R.string.cancel)); + } } } @@ -594,6 +602,53 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, ProfileViewerDialog pvd = ProfileViewerDialog.newInstance(System.currentTimeMillis()); FragmentManager manager = getFragmentManager(); pvd.show(manager, "ProfileViewDialog"); + } else if (item.getTitle().equals(MainApp.gs(R.string.eatingsoon))) { + DefaultValueHelper defHelper = new DefaultValueHelper(); + double target = defHelper.determineEatingSoonTT(profile.getUnits()); + TempTarget tempTarget = new TempTarget() + .date(System.currentTimeMillis()) + .duration(defHelper.determineEatingSoonTTDuration()) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(target) + .high(target); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + } else if (item.getTitle().equals(MainApp.gs(R.string.activity))) { + DefaultValueHelper defHelper = new DefaultValueHelper(); + double target = defHelper.determineActivityTT(profile.getUnits()); + TempTarget tempTarget = new TempTarget() + .date(now()) + .duration(defHelper.determineActivityTTDuration()) + .reason(MainApp.gs(R.string.activity)) + .source(Source.USER) + .low(target) + .high(target); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + } else if (item.getTitle().equals(MainApp.gs(R.string.hypo))) { + DefaultValueHelper defHelper = new DefaultValueHelper(); + double target = defHelper.determineHypoTT(profile.getUnits()); + TempTarget tempTarget = new TempTarget() + .date(now()) + .duration(defHelper.determineHypoTTDuration()) + .reason(MainApp.gs(R.string.activity)) + .source(Source.USER) + .low(target) + .high(target); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + } else if (item.getTitle().equals(MainApp.gs(R.string.custom))) { + NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog(); + final OptionsToShow temptarget = CareportalFragment.TEMPTARGET; + temptarget.executeTempTarget = true; + newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget); + newTTDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); + } else if (item.getTitle().equals(MainApp.gs(R.string.cancel))) { + TempTarget tempTarget = new TempTarget() + .source(Source.USER) + .date(now()) + .duration(0) + .low(0) + .high(0); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } return super.onContextItemSelected(item); @@ -859,6 +914,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, sLoopHandler.removeCallbacksAndMessages(null); unregisterForContextMenu(apsModeView); unregisterForContextMenu(activeProfileView); + unregisterForContextMenu(tempTargetView); } @Override @@ -872,6 +928,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); registerForContextMenu(apsModeView); registerForContextMenu(activeProfileView); + registerForContextMenu(tempTargetView); updateGUI("onResume"); } @@ -1043,12 +1100,18 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, arrowView.setTextColor(color); GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); if (glucoseStatus != null) { - deltaView.setText("Δ " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units); + if (deltaView != null) + deltaView.setText("Δ " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units); + if (deltaShortView != null) + deltaShortView.setText(Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); if (avgdeltaView != null) avgdeltaView.setText("øΔ15m: " + Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units) + " øΔ40m: " + Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)); } else { - deltaView.setText("Δ " + MainApp.gs(R.string.notavailable)); + if (deltaView != null) + deltaView.setText("Δ " + MainApp.gs(R.string.notavailable)); + if (deltaShortView != null) + deltaShortView.setText("---"); if (avgdeltaView != null) avgdeltaView.setText(""); } @@ -1204,7 +1267,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, extendedBolusView.setText(extendedBolusText); } if (extendedBolusText.equals("")) - extendedBolusView.setVisibility(View.GONE); + extendedBolusView.setVisibility(shorttextmode ? View.INVISIBLE : View.GONE); else extendedBolusView.setVisibility(View.VISIBLE); } @@ -1218,17 +1281,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextDefault)); } - tempTargetView.setOnLongClickListener(view -> { - view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog(); - final OptionsToShow temptarget = CareportalFragment.TEMPTARGET; - temptarget.executeTempTarget = true; - newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget); - newTTDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - return true; - }); - tempTargetView.setLongClickable(true); - // QuickWizard button QuickWizardEntry quickWizardEntry = OverviewPlugin.getPlugin().quickWizard.getActive(); if (quickWizardEntry != null && lastBG != null && pump.isInitialized() && !pump.isSuspended()) { @@ -1288,7 +1340,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, flag &= ~Paint.STRIKE_THRU_TEXT_FLAG; bgView.setPaintFlags(flag); - timeAgoView.setText(DateUtil.minAgo(lastBG.date)); + if (timeAgoView != null) + timeAgoView.setText(DateUtil.minAgo(lastBG.date)); + if (timeAgoShortView != null) + timeAgoShortView.setText("(" + DateUtil.minAgoShort(lastBG.date) + ")"); // iob TreatmentsPlugin.getPlugin().updateTotalIOBTreatments(); @@ -1379,7 +1434,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } } - final boolean predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + boolean predictionsAvailable; + if (Config.APS) + predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + else if (Config.NSCLIENT) + predictionsAvailable = true; + else + predictionsAvailable = false; + final boolean finalPredictionsAvailable = predictionsAvailable; + // pump status from ns if (pumpDeviceStatusView != null) { pumpDeviceStatusView.setText(NSDeviceStatus.getInstance().getPumpStatus()); @@ -1398,6 +1461,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, uploaderDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.uploader), NSDeviceStatus.getInstance().getExtendedUploaderStatus(), null)); } + // Sensitivity + if (sensitivityView != null) { + AutosensResult lastAutosensResult = IobCobCalculatorPlugin.getPlugin().detectSensitivityWithLock(IobCobCalculatorPlugin.getPlugin().oldestDataAvailable(), System.currentTimeMillis()); + if (lastAutosensResult != null) + sensitivityView.setText(String.format("%.0f%%", lastAutosensResult.ratio * 100)); + else + sensitivityView.setText(""); + } + // ****** GRAPH ******* new Thread(() -> { @@ -1413,8 +1485,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, final long toTime; final long fromTime; final long endTime; - if (predictionsAvailable && SP.getBoolean("showprediction", false)) { - int predHours = (int) (Math.ceil(finalLastRun.constraintsProcessed.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); + + APSResult apsResult = null; + + if (finalPredictionsAvailable && SP.getBoolean("showprediction", false)) { + if (Config.APS) + apsResult = finalLastRun.constraintsProcessed; + else + apsResult = NSDeviceStatus.getAPSResult(); + int predHours = (int) (Math.ceil(apsResult.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); predHours = Math.min(2, predHours); predHours = Math.max(0, predHours); hoursToFetch = rangeToDisplay - predHours; @@ -1440,9 +1519,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, graphData.addInRangeArea(fromTime, endTime, lowLine, highLine); // **** BG **** - if (predictionsAvailable && SP.getBoolean("showprediction", false)) + if (finalPredictionsAvailable && SP.getBoolean("showprediction", false)) graphData.addBgReadings(fromTime, toTime, lowLine, highLine, - finalLastRun.constraintsProcessed.getPredictions()); + apsResult.getPredictions()); else graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null); @@ -1494,7 +1573,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, secondGraphData.addDeviations(fromTime, now, useDevForScale, 1d); if (SP.getBoolean("showratios", false)) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1d); - if (SP.getBoolean("showdevslope", false)) + if (SP.getBoolean("showdevslope", false) && MainApp.devBranch) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1d); // **** NOW line **** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java deleted file mode 100644 index 49ccf9fbfb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java +++ /dev/null @@ -1,15 +0,0 @@ -package info.nightscout.androidaps.plugins.Overview.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 02.07.2017. - */ - -public class EventSetWakeLock extends Event { - public boolean lock = false; - - public EventSetWakeLock(boolean val) { - lock = val; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index 30e2ad97e4..24ba8c1cea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -49,7 +49,8 @@ import info.nightscout.utils.Round; public class GraphData { private GraphView graph; - public double maxY = 0; + public double maxY = Double.MIN_VALUE; + public double minY = Double.MAX_VALUE; private List bgReadingsArray; private String units; private List series = new ArrayList<>(); @@ -63,7 +64,7 @@ public class GraphData { } public void addBgReadings(long fromTime, long toTime, double lowLine, double highLine, List predictions) { - double maxBgValue = 0d; + double maxBgValue = Double.MIN_VALUE; bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true); List bgListArray = new ArrayList<>(); @@ -93,10 +94,8 @@ public class GraphData { maxY = maxBgValue; + minY = 0; // set manual y bounds to have nice steps - graph.getViewport().setMaxY(maxY); - graph.getViewport().setMinY(0); - graph.getViewport().setYAxisBoundsManual(true); graph.getGridLabelRenderer().setNumVerticalLabels(numOfVertLines); addSeries(new PointsWithLabelGraphSeries<>(bg)); @@ -335,7 +334,7 @@ public class GraphData { public void addIob(long fromTime, long toTime, boolean useForScale, double scale) { FixedLineGraphSeries iobSeries; List iobArray = new ArrayList<>(); - Double maxIobValueFound = 0d; + Double maxIobValueFound = Double.MIN_VALUE; double lastIob = 0; Scale iobScale = new Scale(); @@ -361,8 +360,10 @@ public class GraphData { iobSeries.setColor(MainApp.gc(R.color.iob)); iobSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxIobValueFound; + minY = -maxIobValueFound; + } iobScale.setMultiplier(maxY * scale / maxIobValueFound); @@ -406,8 +407,10 @@ public class GraphData { cobSeries.setColor(MainApp.gc(R.color.cob)); cobSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxCobValueFound; + minY = 0; + } cobScale.setMultiplier(maxY * scale / maxCobValueFound); @@ -438,9 +441,18 @@ public class GraphData { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getAutosensData(time); if (autosensData != null) { int color = MainApp.gc(R.color.deviationblack); // "=" - if (autosensData.pastSensitivity.equals("C")) color = MainApp.gc(R.color.deviationgrey); - if (autosensData.pastSensitivity.equals("+")) color = MainApp.gc(R.color.deviationgreen); - if (autosensData.pastSensitivity.equals("-")) color = MainApp.gc(R.color.deviationred); + if (autosensData.type.equals("") || autosensData.type.equals("non-meal")) { + if (autosensData.pastSensitivity.equals("C")) + color = MainApp.gc(R.color.deviationgrey); + if (autosensData.pastSensitivity.equals("+")) + color = MainApp.gc(R.color.deviationgreen); + if (autosensData.pastSensitivity.equals("-")) + color = MainApp.gc(R.color.deviationred); + } else if (autosensData.type.equals("uam")) { + color = MainApp.gc(R.color.uam); + } else if (autosensData.type.equals("csf")) { + color = MainApp.gc(R.color.deviationgrey); + } devArray.add(new DeviationDataPoint(time, autosensData.deviation, color, devScale)); maxDevValueFound = Math.max(maxDevValueFound, Math.abs(autosensData.deviation)); } @@ -457,8 +469,10 @@ public class GraphData { } }); - if (useForScale) + if (useForScale) { maxY = maxDevValueFound; + minY = -maxY; + } devScale.setMultiplier(maxY * scale / maxDevValueFound); @@ -469,14 +483,16 @@ public class GraphData { public void addRatio(long fromTime, long toTime, boolean useForScale, double scale) { LineGraphSeries ratioSeries; List ratioArray = new ArrayList<>(); - Double maxRatioValueFound = 0d; - Scale ratioScale = new Scale(-1d); + Double maxRatioValueFound = Double.MIN_VALUE; + Double minRatioValueFound = Double.MAX_VALUE; + Scale ratioScale = new Scale(); for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getAutosensData(time); if (autosensData != null) { - ratioArray.add(new ScaledDataPoint(time, autosensData.autosensRatio, ratioScale)); - maxRatioValueFound = Math.max(maxRatioValueFound, Math.abs(autosensData.autosensRatio)); + ratioArray.add(new ScaledDataPoint(time, autosensData.autosensRatio - 1, ratioScale)); + maxRatioValueFound = Math.max(maxRatioValueFound, autosensData.autosensRatio - 1); + minRatioValueFound = Math.min(minRatioValueFound, autosensData.autosensRatio - 1); } } @@ -487,8 +503,10 @@ public class GraphData { ratioSeries.setColor(MainApp.gc(R.color.ratio)); ratioSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxRatioValueFound; + minY = minRatioValueFound; + } ratioScale.setMultiplier(maxY * scale / maxRatioValueFound); @@ -529,8 +547,10 @@ public class GraphData { dsMinSeries.setColor(MainApp.gc(R.color.devslopeneg)); dsMinSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = Math.max(maxFromMaxValueFound, maxFromMinValueFound); + minY = -maxY; + } dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound); dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound); @@ -584,6 +604,10 @@ public class GraphData { } } + graph.getViewport().setMaxY(Round.ceilTo(maxY, 1d)); + graph.getViewport().setMinY(Round.floorTo(minY, 1d)); + graph.getViewport().setYAxisBoundsManual(true); + // draw it graph.onDataChanged(false, false); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java index 5f39cedafe..4a87a8508e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java @@ -312,20 +312,20 @@ public class PointsWithLabelGraphSeries e mPaint.setStrokeWidth(5); canvas.drawRect(px - 3, bounds.top + py - 3, xpluslength + 3, bounds.bottom + py + 3, mPaint); } - } else if (value.getShape() == Shape.OPENAPSOFFLINE) { + } else if (value.getShape() == Shape.OPENAPSOFFLINE && value.getDuration() != 0) { mPaint.setStrokeWidth(0); if (value.getLabel() != null) { - mPaint.setStrokeWidth(0); - mPaint.setTextSize(scaledTextSize); - mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + //mPaint.setStrokeWidth(0); + //mPaint.setTextSize(scaledTextSize); + //mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); Rect bounds = new Rect(); - mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds); - mPaint.setStyle(Paint.Style.STROKE); + //mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds); + mPaint.setStyle(Paint.Style.FILL_AND_STROKE); float px = endX; float py = graphTop + 50; - canvas.drawText(value.getLabel(), px, py, mPaint); + //canvas.drawText(value.getLabel(), px, py, mPaint); mPaint.setStrokeWidth(5); - canvas.drawRect(px - 3, bounds.top + py - 3, xpluslength + 3, bounds.bottom + py + 3, mPaint); + canvas.drawRect(px - 3, graphTop, xpluslength + 3, graphTop + graphHeight, mPaint); } } else if (value.getShape() == Shape.GENERALWITHDURATION) { mPaint.setStrokeWidth(0); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/DummyService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/DummyService.java new file mode 100644 index 0000000000..fad1147dfc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/DummyService.java @@ -0,0 +1,31 @@ +package info.nightscout.androidaps.plugins.Persistentnotification; + +import android.app.Notification; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.support.annotation.Nullable; + +/** + * Keeps AndroidAPS in foreground state, so it won't be terminated by Android nor get restricted by the background execution limits + */ +public class DummyService extends Service { + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Notification notification = PersistentNotificationPlugin.getPlugin().updateNotification(); + if (notification != null) + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); + return START_STICKY; + } + + @Override + public void onDestroy() { + stopForeground(true); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java index 5f6d50273a..3fc7371311 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java @@ -1,6 +1,7 @@ package info.nightscout.androidaps.plugins.Persistentnotification; import android.annotation.SuppressLint; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; @@ -35,7 +36,6 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.IobCobCalculator.CobInfo; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DecimalFormatter; @@ -46,9 +46,16 @@ import info.nightscout.utils.DecimalFormatter; public class PersistentNotificationPlugin extends PluginBase { + private static PersistentNotificationPlugin plugin; + + public static PersistentNotificationPlugin getPlugin() { + if (plugin == null) plugin = new PersistentNotificationPlugin(MainApp.instance()); + return plugin; + } + public static final String CHANNEL_ID = "AndroidAPS-Ongoing"; - private static final int ONGOING_NOTIFICATION_ID = 4711; + public static final int ONGOING_NOTIFICATION_ID = 4711; private final Context ctx; public PersistentNotificationPlugin(Context ctx) { @@ -57,6 +64,7 @@ public class PersistentNotificationPlugin extends PluginBase { .neverVisible(true) .pluginName(R.string.ongoingnotificaction) .enableByDefault(true) + .alwaysEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) .description(R.string.description_persistent_notification) ); this.ctx = ctx; @@ -66,7 +74,7 @@ public class PersistentNotificationPlugin extends PluginBase { protected void onStart() { MainApp.bus().register(this); createNotificationChannel(); - updateNotification(); + triggerNotificationUpdate(); super.onStart(); } @@ -85,20 +93,22 @@ public class PersistentNotificationPlugin extends PluginBase { @Override protected void onStop() { MainApp.bus().unregister(this); - NotificationManager mNotificationManager = - (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(ONGOING_NOTIFICATION_ID); + MainApp.instance().stopService(new Intent(MainApp.instance(), DummyService.class)); } - private void updateNotification() { + private void triggerNotificationUpdate() { + MainApp.instance().startService(new Intent(MainApp.instance(), DummyService.class)); + } + + Notification updateNotification() { if (!isEnabled(PluginType.GENERAL)) { - return; + return null; } String line1 = ""; if (MainApp.getConfigBuilder().getActiveProfileInterface() == null || !MainApp.getConfigBuilder().isProfileValid("Notificiation")) - return; + return null; String units = MainApp.getConfigBuilder().getProfileUnits(); @@ -166,7 +176,7 @@ public class PersistentNotificationPlugin extends PluginBase { android.app.Notification notification = builder.build(); mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification); - + return notification; } private String deltastring(double deltaMGDL, double deltaMMOL, String units) { @@ -188,42 +198,42 @@ public class PersistentNotificationPlugin extends PluginBase { @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventExtendedBolusChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventNewBG ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventInitializationChanged ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { - updateNotification(); + triggerNotificationUpdate(); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java index 39d0f9829d..18060ba1ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java @@ -132,7 +132,7 @@ public class LocalProfileFragment extends SubscriberFragment { LocalProfilePlugin.getPlugin().loadSettings(); mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - diaView.setParams(LocalProfilePlugin.getPlugin().dia, 2d, 48d, 0.1d, new DecimalFormat("0.0"), false, textWatch); + diaView.setParams(LocalProfilePlugin.getPlugin().dia, 5d, 12d, 0.1d, new DecimalFormat("0.0"), false, textWatch); icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save); isfView = new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save); basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java index dca9b183a3..6cc9fdf98e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java @@ -79,11 +79,14 @@ public class NSProfileFragment extends SubscriberFragment { public void onStatusEvent(final EventNSProfileUpdateGUI ev) { Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(() -> updateGUI()); + activity.runOnUiThread(() -> { synchronized (NSProfileFragment.this) { updateGUI(); } }); } @Override protected void updateGUI() { + if (noProfile == null || profileSpinner == null) + return; + ProfileStore profileStore = NSProfilePlugin.getPlugin().getProfile(); if (profileStore != null) { ArrayList profileList = profileStore.getProfileList(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java index d4aa0d7da8..2555eac427 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java @@ -1,8 +1,11 @@ package info.nightscout.androidaps.plugins.PumpCombo; +import android.content.DialogInterface; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.crashlytics.android.answers.CustomEvent; @@ -28,6 +31,7 @@ import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TDD; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventRefreshOverview; @@ -194,6 +198,32 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint return MainApp.gs(R.string.combo_pump_state_running); } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + + @Override public boolean isInitialized() { return pump.initialized; @@ -354,6 +384,11 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint // trigger a connect, which will update state and check history CommandResult stateResult = runCommand(null, 1, ruffyScripter::readPumpState); + if (stateResult.invalidSetup) { + MainApp.bus().post(new EventNewNotification( + new Notification(Notification.COMBO_PUMP_ALARM, MainApp.gs(R.string.combo_invalid_setup), Notification.URGENT))); + return; + } if (!stateResult.success) { return; } @@ -1374,7 +1409,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint private boolean validBasalRateProfileSelectedOnPump = true; @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!validBasalRateProfileSelectedOnPump) value.set(false, MainApp.gs(R.string.novalidbasalrate), this); return value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java index 9da5d4d6fa..b595d13d28 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java @@ -5,10 +5,11 @@ import android.support.annotation.Nullable; import java.util.LinkedList; import java.util.List; -import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.Bolus; import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.PumpHistory; public class CommandResult { + /** True if a condition indicating a broken pump setup/configuration is detected */ + public boolean invalidSetup; /** Whether the command was executed successfully. */ public boolean success; /** State of the pump *after* command execution. */ diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java index 1e7e542e5f..5b569e479e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java @@ -281,10 +281,10 @@ public class RuffyScripter implements RuffyCommands { log.debug("Executing " + cmd + " took " + (cmdEndTime - cmdStartTime) + "ms"); } catch (CommandException e) { log.error("CommandException running command", e); - activeCmd.getResult().success = false; + cmd.getResult().success = false; } catch (Exception e) { log.error("Unexpected exception running cmd", e); - activeCmd.getResult().success = false; + cmd.getResult().success = false; } }, cmd.getClass().getSimpleName()); long executionStart = System.currentTimeMillis(); @@ -328,6 +328,7 @@ public class RuffyScripter implements RuffyCommands { if (unparsableMenuEncountered) { log.error("UnparsableMenuEncountered flagged, aborting command"); cmdThread.interrupt(); + activeCmd.getResult().invalidSetup = true; activeCmd.getResult().success = false; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java index e55fb4d010..654861fddb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java @@ -39,17 +39,19 @@ public class ReadQuickInfoCommand extends BaseCommand { // read bolus records int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD); int record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); - while (true) { - bolusHistory.add(readBolusRecord()); - if (bolusHistory.size() == numberOfBolusRecordsToRetrieve || record == totalRecords) { - break; + if (record > 0) { + while (true) { + bolusHistory.add(readBolusRecord()); + if (bolusHistory.size() == numberOfBolusRecordsToRetrieve || record == totalRecords) { + break; + } + // advance to next record + scripter.pressDownKey(); + while (record == (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD)) { + scripter.waitForScreenUpdate(); + } + record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); } - // advance to next record - scripter.pressDownKey(); - while (record == (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD)) { - scripter.waitForScreenUpdate(); - } - record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); } if (log.isDebugEnabled()) { if (!result.history.bolusHistory.isEmpty()) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java index 8d18ca4f04..7288602d65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java @@ -216,74 +216,96 @@ public class DanaRFragment extends SubscriberFragment { @SuppressLint("SetTextI18n") @Override public void run() { - DanaRPump pump = DanaRPump.getInstance(); - if (pump.lastConnection != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastConnection; - int agoMin = (int) (agoMsec / 60d / 1000d); - lastConnectionView.setText(DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")"); - SetWarnColor.setColor(lastConnectionView, agoMin, 16d, 31d); - } - if (pump.lastBolusTime.getTime() != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastBolusTime.getTime(); - double agoHours = agoMsec / 60d / 60d / 1000d; - if (agoHours < 6) // max 6h back - lastBolusView.setText(DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime.getTime()) + " " + DecimalFormatter.to2Decimal(DanaRPump.getInstance().lastBolusAmount) + " U"); - else lastBolusView.setText(""); - } + synchronized(DanaRFragment.this) { + if (!isBound()) return; - dailyUnitsView.setText(DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U"); - SetWarnColor.setColor(dailyUnitsView, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75d, pump.maxDailyTotalUnits * 0.9d); - basaBasalRateView.setText("( " + (pump.activeProfile + 1) + " ) " + DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) + " U/h"); - // DanaRPlugin, DanaRKoreanPlugin - if (ConfigBuilderPlugin.getActivePump().isFakingTempsByExtendedBoluses()) { - if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) { - tempBasalView.setText(TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); - } else { - tempBasalView.setText(""); + DanaRPump pump = DanaRPump.getInstance(); + if (pump.lastConnection != 0) { + Long agoMsec = System.currentTimeMillis() - pump.lastConnection; + int agoMin = (int) (agoMsec / 60d / 1000d); + lastConnectionView.setText(DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")"); + SetWarnColor.setColor(lastConnectionView, agoMin, 16d, 31d); } - } else { - // v2 plugin - if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { - tempBasalView.setText(TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); - } else { - tempBasalView.setText(""); + if (pump.lastBolusTime.getTime() != 0) { + Long agoMsec = System.currentTimeMillis() - pump.lastBolusTime.getTime(); + double agoHours = agoMsec / 60d / 60d / 1000d; + if (agoHours < 6) // max 6h back + lastBolusView.setText(DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime.getTime()) + " " + DecimalFormatter.to2Decimal(DanaRPump.getInstance().lastBolusAmount) + " U"); + else lastBolusView.setText(""); } - } - ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); - if (activeExtendedBolus != null) { - extendedBolusView.setText(activeExtendedBolus.toString()); - } else { - extendedBolusView.setText(""); - } - reservoirView.setText(DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + " / 300 U"); - SetWarnColor.setColorInverse(reservoirView, pump.reservoirRemainingUnits, 50d, 20d); - batteryView.setText("{fa-battery-" + (pump.batteryRemaining / 25) + "}"); - SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d); - iobView.setText(pump.iob + " U"); - if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { - firmwareView.setText(String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode)); - } else { - firmwareView.setText("OLD"); - } - basalStepView.setText("" + pump.basalStep); - bolusStepView.setText("" + pump.bolusStep); - serialNumberView.setText("" + pump.serialNumber); - if (queueView != null) { - Spanned status = ConfigBuilderPlugin.getCommandQueue().spannedStatus(); - if (status.toString().equals("")) { - queueView.setVisibility(View.GONE); + + dailyUnitsView.setText(DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U"); + SetWarnColor.setColor(dailyUnitsView, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75d, pump.maxDailyTotalUnits * 0.9d); + basaBasalRateView.setText("( " + (pump.activeProfile + 1) + " ) " + DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) + " U/h"); + // DanaRPlugin, DanaRKoreanPlugin + if (ConfigBuilderPlugin.getActivePump().isFakingTempsByExtendedBoluses()) { + if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) { + tempBasalView.setText(TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); + } else { + tempBasalView.setText(""); + } } else { - queueView.setVisibility(View.VISIBLE); - queueView.setText(status); + // v2 plugin + if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { + tempBasalView.setText(TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); + } else { + tempBasalView.setText(""); + } + } + ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); + if (activeExtendedBolus != null) { + extendedBolusView.setText(activeExtendedBolus.toString()); + } else { + extendedBolusView.setText(""); + } + reservoirView.setText(DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + " / 300 U"); + SetWarnColor.setColorInverse(reservoirView, pump.reservoirRemainingUnits, 50d, 20d); + batteryView.setText("{fa-battery-" + (pump.batteryRemaining / 25) + "}"); + SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d); + iobView.setText(pump.iob + " U"); + if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { + firmwareView.setText(String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode)); + } else { + firmwareView.setText("OLD"); + } + basalStepView.setText("" + pump.basalStep); + bolusStepView.setText("" + pump.bolusStep); + serialNumberView.setText("" + pump.serialNumber); + if (queueView != null) { + Spanned status = ConfigBuilderPlugin.getCommandQueue().spannedStatus(); + if (status.toString().equals("")) { + queueView.setVisibility(View.GONE); + } else { + queueView.setVisibility(View.VISIBLE); + queueView.setText(status); + } + } + //hide user options button if not an RS pump + boolean isKorean = MainApp.getSpecificPlugin(DanaRKoreanPlugin.class) != null && MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isEnabled(PluginType.PUMP); + if (isKorean) { + danar_user_options.setVisibility(View.GONE); } - } - //hide user options button if not an RS pump - boolean isKorean = MainApp.getSpecificPlugin(DanaRKoreanPlugin.class) != null && MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isEnabled(PluginType.PUMP); - if (isKorean ) { - danar_user_options.setVisibility(View.GONE); } } }); } + private boolean isBound() { + return lastConnectionView != null + && lastBolusView != null + && dailyUnitsView != null + && basaBasalRateView != null + && tempBasalView != null + && extendedBolusView != null + && reservoirView != null + && batteryView != null + && iobView != null + && firmwareView != null + && basalStepView != null + && bolusStepView != null + && serialNumberView != null + && danar_user_options != null + && queueView != null; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index b1b0f3d9ce..77146738d9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaR; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -18,6 +21,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStartWithSpeed; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; @@ -78,6 +82,31 @@ public class DanaRPlugin extends AbstractDanaRPlugin { pumpDescription.needsManualTDDLoad = true; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index b7b100a3a6..9ce1a6409a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaRKorean; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -23,6 +26,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.PumpDanaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStart; import info.nightscout.androidaps.plugins.PumpDanaRKorean.services.DanaRKoreanExecutionService; @@ -45,7 +49,8 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { } public DanaRKoreanPlugin() { - super(); + pluginDescription.description(R.string.description_pump_dana_r_korean); + log = LoggerFactory.getLogger(DanaRKoreanPlugin.class); useExtendedBoluses = SP.getBoolean("danar_useextended", false); @@ -79,6 +84,32 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { pumpDescription.needsManualTDDLoad = true; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java index c2c438b50c..96b6f393cd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java @@ -2,10 +2,13 @@ package info.nightscout.androidaps.plugins.PumpDanaRS; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -36,6 +39,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; @@ -147,6 +151,31 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte MainApp.bus().unregister(this); } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { @@ -494,8 +523,12 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte // Convert duration from minutes to hours if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); - // use special APS temp basal call ... 100+/15min .... 100-/30min - result = setHighTempBasalPercent(percentRate); + if (percentRate == 0 && durationInMinutes > 30) { + result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + } else { + // use special APS temp basal call ... 100+/15min .... 100-/30min + result = setHighTempBasalPercent(percentRate); + } if (!result.success) { log.error("setTempBasalAbsolute: Failed to set hightemp basal"); return result; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java index b552026279..5b6afb8089 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java @@ -20,8 +20,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import info.nightscout.androidaps.MainApp; @@ -45,11 +43,11 @@ public class BLEComm { private static final long WRITE_DELAY_MILLIS = 50; - public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; - public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; + private static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; + private static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; - private byte PACKET_START_BYTE = (byte) 0xA5; - private byte PACKET_END_BYTE = (byte) 0x5A; + private final byte PACKET_START_BYTE = (byte) 0xA5; + private final byte PACKET_END_BYTE = (byte) 0x5A; private static BLEComm instance = null; public static BLEComm getInstance(DanaRSService service) { @@ -58,16 +56,13 @@ public class BLEComm { return instance; } - private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture scheduledDisconnection = null; private DanaRS_Packet processsedMessage = null; - private ArrayList mSendQueue = new ArrayList<>(); + private final ArrayList mSendQueue = new ArrayList<>(); private BluetoothManager mBluetoothManager = null; private BluetoothAdapter mBluetoothAdapter = null; - private BluetoothDevice mBluetoothDevice = null; - private String mBluetoothDeviceAddress = null; private String mBluetoothDeviceName = null; private BluetoothGatt mBluetoothGatt = null; @@ -79,7 +74,7 @@ public class BLEComm { private DanaRSService service; - BLEComm(DanaRSService service) { + private BLEComm(DanaRSService service) { this.service = service; initialize(); } @@ -138,15 +133,13 @@ public class BLEComm { BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { - log.debug("Device not found. Unable to connect."); + log.debug("Device not found. Unable to connect from: " + from); return false; } - log.debug("Trying to create a new connection."); + log.debug("Trying to create a new connection from: " + from); mBluetoothGatt = device.connectGatt(service.getApplicationContext(), false, mGattCallback); setCharacteristicNotification(getUARTReadBTGattChar(), true); - mBluetoothDevice = device; - mBluetoothDeviceAddress = address; mBluetoothDeviceName = device.getName(); return true; } @@ -174,7 +167,7 @@ public class BLEComm { SystemClock.sleep(2000); } - public void close() { + public synchronized void close() { log.debug("BluetoothAdapter close"); if (mBluetoothGatt == null) { return; @@ -184,15 +177,8 @@ public class BLEComm { mBluetoothGatt = null; } - public BluetoothDevice getConnectDevice() { - return mBluetoothDevice; - } - public String getConnectDeviceAddress() { - return mBluetoothDeviceAddress; - } - - public String getConnectDeviceName() { + private String getConnectDeviceName() { return mBluetoothDeviceName; } @@ -229,33 +215,25 @@ public class BLEComm { public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); addToReadBuffer(characteristic.getValue()); - new Thread(new Runnable() { - @Override - public void run() { - readDataParsing(); - } - }).start(); + new Thread(() -> readDataParsing()).start(); } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); - new Thread(new Runnable() { - @Override - public void run() { - synchronized (mSendQueue) { - // after message sent, check if there is the rest of the message waiting and send it - if (mSendQueue.size() > 0) { - byte[] bytes = mSendQueue.get(0); - mSendQueue.remove(0); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } + new Thread(() -> { + synchronized (mSendQueue) { + // after message sent, check if there is the rest of the message waiting and send it + if (mSendQueue.size() > 0) { + byte[] bytes = mSendQueue.get(0); + mSendQueue.remove(0); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); } } }).start(); } }; - public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { + private synchronized void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { log.debug("setCharacteristicNotification"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -266,7 +244,7 @@ public class BLEComm { mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); } - public void readCharacteristic(BluetoothGattCharacteristic characteristic) { + public synchronized void readCharacteristic(BluetoothGattCharacteristic characteristic) { log.debug("readCharacteristic"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -277,41 +255,39 @@ public class BLEComm { mBluetoothGatt.readCharacteristic(characteristic); } - public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { - new Thread(new Runnable() { - public void run() { - SystemClock.sleep(WRITE_DELAY_MILLIS); + private synchronized void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { + new Thread(() -> { + SystemClock.sleep(WRITE_DELAY_MILLIS); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - isConnecting = false; - isConnected = false; - return; - } - - characteristic.setValue(data); - characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); - log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); - mBluetoothGatt.writeCharacteristic(characteristic); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + isConnecting = false; + isConnected = false; + return; } + + characteristic.setValue(data); + characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); + log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); + mBluetoothGatt.writeCharacteristic(characteristic); }).start(); } - public BluetoothGattCharacteristic getUARTReadBTGattChar() { + private BluetoothGattCharacteristic getUARTReadBTGattChar() { if (UART_Read == null) { UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0); } return UART_Read; } - public BluetoothGattCharacteristic getUARTWriteBTGattChar() { + private BluetoothGattCharacteristic getUARTWriteBTGattChar() { if (UART_Write == null) { UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0); } return UART_Write; } - public List getSupportedGattServices() { + private List getSupportedGattServices() { log.debug("getSupportedGattServices"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -329,7 +305,7 @@ public class BLEComm { if (gattServices == null) { return; } - String uuid = null; + String uuid; for (BluetoothGattService gattService : gattServices) { List gattCharacteristics = gattService.getCharacteristics(); @@ -346,7 +322,7 @@ public class BLEComm { } } - private byte[] readBuffer = new byte[1024]; + private final byte[] readBuffer = new byte[1024]; private int bufferLength = 0; private void addToReadBuffer(byte[] buffer) { @@ -634,7 +610,7 @@ public class BLEComm { writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); } - protected void SendPumpCheck() { + private void SendPumpCheck() { // 1st message sent to pump after connect byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName()); log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java index 63035d64d0..ff4203fd7b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java @@ -19,6 +19,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Get_User_Option; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; @@ -135,6 +136,37 @@ public class DanaRSService extends Service { bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus, bolusStep, maxBolus MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + + long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + if (Math.abs(timeDiff) > 3) { + if (Math.abs(timeDiff) > 60*60*1.5) { + log.debug("Pump time difference: " + timeDiff + " seconds - large difference"); + //If time-diff is very large, warn user until we can synchronize history readings properly + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.error); + i.putExtra("status", MainApp.gs(R.string.largetimediff)); + i.putExtra("title", MainApp.gs(R.string.largetimedifftitle)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + //deinitialize pump + danaRPump.lastConnection = 0; + MainApp.bus().post(new EventDanaRNewStatus()); + MainApp.bus().post(new EventInitializationChanged()); + return; + } else { + waitForWholeMinute(); // Dana can set only whole minute + // add 10sec to be sure we are over minute (will be cutted off anyway) + bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + } + } + danaRPump.lastConnection = System.currentTimeMillis(); long now = System.currentTimeMillis(); if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()) { @@ -146,19 +178,7 @@ public class DanaRSService extends Service { bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); - bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); bleComm.sendMessage(new DanaRS_Packet_Option_Get_User_Option()); // Getting user options - long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - if (Math.abs(timeDiff) > 3) { - waitForWholeMinute(); // Dana can set only whole minute - // add 10sec to be sure we are over minute (will be cutted off anyway) - bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); - bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); - timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - } danaRPump.lastSettingsRead = now; } @@ -183,6 +203,13 @@ public class DanaRSService extends Service { } public PumpEnactResult loadEvents() { + + if(!MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()){ + PumpEnactResult result = new PumpEnactResult().success(false); + result.comment = "pump not initialized"; + return result; + } + DanaRS_Packet_APS_History_Events msg; if (lastHistoryFetched == 0) { msg = new DanaRS_Packet_APS_History_Events(0); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index b4004fecb3..c88d62ecc7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaRv2; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -20,6 +23,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PumpDescription; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.PumpDanaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStartWithSpeed; @@ -44,6 +48,8 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { } private DanaRv2Plugin() { + pluginDescription.description(R.string.description_pump_dana_r_v2); + log = LoggerFactory.getLogger(DanaRv2Plugin.class); useExtendedBoluses = false; @@ -138,6 +144,31 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { return pump.lastConnection > 0 && pump.maxBasal > 0; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + // Pump interface @Override public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { @@ -266,8 +297,12 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { // Convert duration from minutes to hours if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); - // use special APS temp basal call ... 100+/15min .... 100-/30min - result = setHighTempBasalPercent(percentRate); + if (percentRate == 0 && durationInMinutes > 30) { + result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + } else { + // use special APS temp basal call ... 100+/15min .... 100-/30min + result = setHighTempBasalPercent(percentRate); + } if (!result.success) { log.error("setTempBasalAbsolute: Failed to set hightemp basal"); return result; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java index 1ada4c629d..6977139395 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java @@ -1,6 +1,7 @@ package info.nightscout.androidaps.plugins.PumpDanaRv2.services; import android.bluetooth.BluetoothDevice; +import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; import android.os.SystemClock; @@ -18,6 +19,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingUserOptions; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetUserOptions; import info.nightscout.androidaps.plugins.Treatments.Treatment; @@ -187,6 +189,36 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(tempStatusMsg); MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); mSerialIOThread.sendMessage(exStatusMsg); + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + long timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + if (Math.abs(timeDiff) > 3) { + if (Math.abs(timeDiff) > 60*60*1.5) { + log.debug("Pump time difference: " + timeDiff + " seconds - large difference"); + //If time-diff is very large, warn user until we can synchronize history readings properly + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.error); + i.putExtra("status", MainApp.gs(R.string.largetimediff)); + i.putExtra("title", MainApp.gs(R.string.largetimedifftitle)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + //deinitialize pump + mDanaRPump.lastConnection = 0; + MainApp.bus().post(new EventDanaRNewStatus()); + MainApp.bus().post(new EventInitializationChanged()); + return; + } else { + waitForWholeMinute(); // Dana can set only whole minute + // add 10sec to be sure we are over minute (will be cutted off anyway) + mSerialIOThread.sendMessage(new MsgSetTime(new Date(DateUtil.now() + T.secs(10).msecs()))); + mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + } + } + mDanaRPump.lastConnection = System.currentTimeMillis(); long now = System.currentTimeMillis(); if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRv2Plugin.class).isInitialized()) { @@ -202,18 +234,6 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(new MsgSettingProfileRatios()); mSerialIOThread.sendMessage(new MsgSettingUserOptions()); mSerialIOThread.sendMessage(new MsgSettingProfileRatiosAll()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); - mSerialIOThread.sendMessage(new MsgSettingPumpTime()); - long timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - if (Math.abs(timeDiff) > 3) { - waitForWholeMinute(); // Dana can set only whole minute - // add 10sec to be sure we are over minute (will be cutted off anyway) - mSerialIOThread.sendMessage(new MsgSetTime(new Date(DateUtil.now() + T.secs(10).msecs()))); - mSerialIOThread.sendMessage(new MsgSettingPumpTime()); - timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - } mDanaRPump.lastSettingsRead = now; } @@ -425,6 +445,14 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { } public PumpEnactResult loadEvents() { + + if(!MainApp.getSpecificPlugin(DanaRv2Plugin.class).isInitialized()){ + PumpEnactResult result = new PumpEnactResult().success(false); + result.comment = "pump not initialized"; + return result; + } + + if (!isConnected()) return new PumpEnactResult().success(false); SystemClock.sleep(300); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java index 2b31e4e3d7..a0e0199c79 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java @@ -1,5 +1,9 @@ package info.nightscout.androidaps.plugins.PumpInsight; +import android.content.DialogInterface; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; + import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -27,6 +31,7 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; @@ -203,6 +208,31 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai return result; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + @Override public boolean isInitialized() { return initialized; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java new file mode 100644 index 0000000000..f05cea1b9d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.plugins.Sensitivity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.SensitivityInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; + +public abstract class AbstractSensitivityPlugin extends PluginBase implements SensitivityInterface { + + private static final Logger log = LoggerFactory.getLogger(SensitivityInterface.class); + + public AbstractSensitivityPlugin(PluginDescription pluginDescription) { + super(pluginDescription); + } + + @Override + public abstract AutosensResult detectSensitivity(long fromTime, long toTime); + + public AutosensResult fillResult(double ratio, double carbsAbsorbed, String pastSensitivity, + String ratioLimit, String sensResult, int deviationsArraySize) { + return this.fillResult(ratio, carbsAbsorbed, pastSensitivity, ratioLimit, sensResult, + deviationsArraySize, + SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7")), + SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); + } + + public AutosensResult fillResult(double ratio, double carbsAbsorbed, String pastSensitivity, + String ratioLimit, String sensResult, int deviationsArraySize, + double ratioMin, double ratioMax) { + double rawRatio = ratio; + ratio = Math.max(ratio, ratioMin); + ratio = Math.min(ratio, ratioMax); + + //If not-excluded data <= MIN_HOURS -> don't do Autosens + //If not-excluded data >= MIN_HOURS_FULL_AUTOSENS -> full Autosens + //Between MIN_HOURS and MIN_HOURS_FULL_AUTOSENS: gradually increase autosens + double autosensContrib = (Math.min(Math.max(MIN_HOURS, deviationsArraySize / 12d), + MIN_HOURS_FULL_AUTOSENS) - MIN_HOURS) / (MIN_HOURS_FULL_AUTOSENS - MIN_HOURS); + ratio = autosensContrib * (ratio - 1) + 1; + + if (autosensContrib != 1d) { + ratioLimit += "(" + deviationsArraySize + " of " + MIN_HOURS_FULL_AUTOSENS * 12 + " values) "; + } + + if (ratio != rawRatio) { + ratioLimit += "Ratio limited from " + rawRatio + " to " + ratio; + log.debug(ratioLimit); + } + + AutosensResult output = new AutosensResult(); + output.ratio = Round.roundTo(ratio, 0.01); + output.carbsAbsorbed = Round.roundTo(carbsAbsorbed, 0.01); + output.pastSensitivity = pastSensitivity; + output.ratioLimit = ratioLimit; + output.sensResult = sensResult; + return output; + } + + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java similarity index 79% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java index d7421a3aa2..fdf85c064e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java @@ -1,11 +1,10 @@ -package info.nightscout.androidaps.plugins.SensitivityAAPS; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -15,6 +14,7 @@ import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -30,7 +30,7 @@ import info.nightscout.utils.SafeParse; * Created by mike on 24.06.2017. */ -public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInterface { +public class SensitivityAAPSPlugin extends AbstractSensitivityPlugin { private static Logger log = LoggerFactory.getLogger(SensitivityAAPSPlugin.class); static SensitivityAAPSPlugin plugin = null; @@ -81,6 +81,8 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + List deviationsArray = new ArrayList<>(); String pastSensitivity = ""; int index = 0; @@ -97,8 +99,21 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte continue; } - if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) - deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(deviation); if (deviationsArray.size() > hoursForDetection * 60 / 5) deviationsArray.remove(0); @@ -139,26 +154,16 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte if (Config.logAutosensData) log.debug(sensResult); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); if (Config.logAutosensData) { - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " percentile: " + percentile + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {}, percentile: {} ratio: {} mealCOB: ", + new Date(toTime).toLocaleString(), + percentile, output.ratio, ratio, current.cob); log.debug("Sensitivity to: deviations " + Arrays.toString(deviations)); } - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java similarity index 76% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java index e4bf00be99..cfe5dd28f6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.SensitivityOref0; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; @@ -14,6 +14,7 @@ import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -29,7 +30,7 @@ import info.nightscout.utils.SafeParse; * Created by mike on 24.06.2017. */ -public class SensitivityOref0Plugin extends PluginBase implements SensitivityInterface { +public class SensitivityOref0Plugin extends AbstractSensitivityPlugin { private static Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); static SensitivityOref0Plugin plugin = null; @@ -54,12 +55,7 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt public AutosensResult detectSensitivity(long fromTime, long toTime) { LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); - String age = SP.getString(R.string.key_age, ""); - int defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_adult))) defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_teenage))) defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_child))) defaultHours = 24; - int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); + int hoursForDetection = 24; long now = System.currentTimeMillis(); Profile profile = MainApp.getConfigBuilder().getProfile(); @@ -81,6 +77,8 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + List deviationsArray = new ArrayList<>(); String pastSensitivity = ""; int index = 0; @@ -97,8 +95,21 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt continue; } - if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) - deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(deviation); if (deviationsArray.size() > hoursForDetection * 60 / 5) deviationsArray.remove(0); @@ -150,24 +161,13 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt ratio = 1 + (basalOff / profile.getMaxDailyBasal()); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); if (Config.logAutosensData) - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {} ratio: {} mealCOB: {}", + new Date(toTime).toLocaleString(), output.ratio, current.cob); - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java new file mode 100644 index 0000000000..f6d5a4ef2a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java @@ -0,0 +1,187 @@ +package info.nightscout.androidaps.plugins.Sensitivity; + +import android.support.v4.util.LongSparseArray; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.interfaces.SensitivityInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; + +/** + * Created by mike on 19.06.2018. + */ + +public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { + private static Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); + + static SensitivityOref1Plugin plugin = null; + + public static SensitivityOref1Plugin getPlugin() { + if (plugin == null) + plugin = new SensitivityOref1Plugin(); + return plugin; + } + + public SensitivityOref1Plugin() { + super(new PluginDescription() + .mainType(PluginType.SENSITIVITY) + .pluginName(R.string.sensitivityoref1) + .shortName(R.string.sensitivity_shortname) + .preferencesId(R.xml.pref_absorption_oref1) + .description(R.string.description_sensitivity_oref1) + ); + } + + @Override + public AutosensResult detectSensitivity(long fromTime, long toTime) { + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + + if (profile == null) { + log.debug("No profile"); + return new AutosensResult(); + } + + if (autosensDataTable == null || autosensDataTable.size() < 4) { + log.debug("No autosens data available"); + return new AutosensResult(); + } + + AutosensData current = IobCobCalculatorPlugin.getPlugin().getAutosensData(toTime); // this is running inside lock already + if (current == null) { + log.debug("No current autosens data available"); + return new AutosensResult(); + } + + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + + List deviationsArray = new ArrayList<>(); + String pastSensitivity = ""; + int index = 0; + while (index < autosensDataTable.size()) { + AutosensData autosensData = autosensDataTable.valueAt(index); + + if (autosensData.time < fromTime) { + index++; + continue; + } + + if (autosensData.time > toTime) { + index++; + continue; + } + + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + deviationsArray.add(deviation); + + for (int i = 0; i < autosensData.extraDeviation.size(); i++) + deviationsArray.add(autosensData.extraDeviation.get(i)); + if (deviationsArray.size() > 96) + deviationsArray.remove(0); + + pastSensitivity += autosensData.pastSensitivity; + int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time); + if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) { + pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")"; + } + index++; + } + + // when we have less than 8h worth of deviation data, add up to 90m of zero deviations + // this dampens any large sensitivity changes detected based on too little data, without ignoring them completely + log.debug("Using most recent " + deviationsArray.size() + " deviations"); + if (deviationsArray.size() < 96) { + int pad = Math.round((1 - deviationsArray.size() / 96) * 18); + log.debug("Adding " + pad + " more zero deviations"); + for (int d = 0; d < pad; d++) { + //process.stderr.write("."); + deviationsArray.add(0d); + } + } + + Double[] deviations = new Double[deviationsArray.size()]; + deviations = deviationsArray.toArray(deviations); + + double sens = profile.getIsf(); + + double ratio = 1; + String ratioLimit = ""; + String sensResult = ""; + + if (Config.logAutosensData) + log.debug("Records: " + index + " " + pastSensitivity); + + Arrays.sort(deviations); + + for (double i = 0.9; i > 0.1; i = i - 0.01) { + if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) >= 0 && IobCobCalculatorPlugin.percentile(deviations, i) < 0) { + if (Config.logAutosensData) + log.debug(Math.round(100 * i) + "% of non-meal deviations negative (>50% = sensitivity)"); + } + if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) > 0 && IobCobCalculatorPlugin.percentile(deviations, i) <= 0) { + if (Config.logAutosensData) + log.debug(Math.round(100 * i) + "% of non-meal deviations negative (>50% = resistance)"); + } + } + double pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50); + double pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.50); + + double basalOff = 0; + + if (pSensitive < 0) { // sensitive + basalOff = pSensitive * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin sensitivity detected"; + } else if (pResistant > 0) { // resistant + basalOff = pResistant * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin resistance detected"; + } else { + sensResult = "Sensitivity normal"; + } + + if (Config.logAutosensData) + log.debug(sensResult); + + ratio = 1 + (basalOff / profile.getMaxDailyBasal()); + + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); + + if (Config.logAutosensData) + log.debug("Sensitivity to: {} ratio: {} mealCOB: {}", + new Date(toTime).toLocaleString(), output.ratio, current.cob); + + return output; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java similarity index 81% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java index 3d33b5ae14..ed185e88f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.SensitivityWeightedAverage; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; @@ -6,11 +6,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; +import java.util.List; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -26,7 +28,7 @@ import info.nightscout.utils.SafeParse; * Created by mike on 24.06.2017. */ -public class SensitivityWeightedAveragePlugin extends PluginBase implements SensitivityInterface { +public class SensitivityWeightedAveragePlugin extends AbstractSensitivityPlugin { private static Logger log = LoggerFactory.getLogger(SensitivityWeightedAveragePlugin.class); private static SensitivityWeightedAveragePlugin plugin = null; @@ -79,6 +81,8 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens return new AutosensResult(); } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + String pastSensitivity = ""; int index = 0; LongSparseArray data = new LongSparseArray<>(); @@ -101,11 +105,24 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens continue; } + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + data.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + //data.append(autosensData.time); long reverseWeight = (toTime - autosensData.time) / (5 * 60 * 1000L); - data.append(reverseWeight, autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + if (autosensData.validDeviation) + data.append(reverseWeight, deviation); //weights += reverseWeight; - //weightedsum += reverseWeight * (autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + //weightedsum += reverseWeight * (autosensData.validDeviation ? autosensData.deviation : 0d); pastSensitivity += autosensData.pastSensitivity; @@ -159,25 +176,13 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens if (Config.logAutosensData) log.debug(sensResult); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - if (Config.logAutosensData) - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, data.size()); if (Config.logAutosensData) - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " weightedaverage: " + average + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {} weightedaverage: {} ratio: {} mealCOB: {}", new Date(toTime).toLocaleString(), + average, output.ratio, current.cob); - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java index 8e1dd06d2f..847ead4482 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.SmsCommunicator; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.os.SystemClock; import android.telephony.SmsManager; import android.telephony.SmsMessage; @@ -47,6 +48,7 @@ import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; +import info.nightscout.utils.T; import info.nightscout.utils.XdripCalibrations; /** @@ -450,12 +452,14 @@ public class SmsCommunicatorPlugin extends PluginBase { public void run() { PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); if (result.success) { + SystemClock.sleep(T.secs(15).msecs()); // wait some time to get history String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), result.bolusDelivered); if (pump != null) reply += "\n" + pump.shortStatus(true); lastRemoteBolusTime = new Date(); sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply, new Date())); } else { + SystemClock.sleep(T.secs(60).msecs()); // wait some time to get history String reply = MainApp.gs(R.string.smscommunicator_bolusfailed); if (pump != null) reply += "\n" + pump.shortStatus(true); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java index e3c4181c05..c99fbcb9bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java @@ -20,6 +20,9 @@ public class SourceNSClientPlugin extends PluginBase implements BgSourceInterfac return plugin; } + private long lastBGTimeStamp = 0; + private boolean isAdvancedFilteringEnabled = false; + private SourceNSClientPlugin() { super(new PluginDescription() .mainType(PluginType.BGSOURCE) @@ -33,6 +36,16 @@ public class SourceNSClientPlugin extends PluginBase implements BgSourceInterfac @Override public boolean advancedFilteringSupported() { - return false; + return isAdvancedFilteringEnabled; + } + + public void detectSource(String source, long timeStamp) { + if (timeStamp > lastBGTimeStamp) { + if (source.contains("G5 Native") || source.contains("AndroidAPS-DexcomG5")) + isAdvancedFilteringEnabled = true; + else + isAdvancedFilteringEnabled = false; + lastBGTimeStamp = timeStamp; + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index a754b7dcfa..461f204f84 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -40,8 +40,8 @@ import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -78,7 +78,6 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .fragmentClass(TreatmentsFragment.class.getName()) .pluginName(R.string.treatments) .shortName(R.string.treatments_shortname) - .preferencesId(R.xml.pref_absorption_oref0) .alwaysEnabled(true) .description(R.string.description_treatments) ); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java index 57ad6833a9..0faceb2382 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java @@ -43,6 +43,8 @@ import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; +import static info.nightscout.utils.DateUtil.now; + public class TreatmentsBolusFragment extends SubscriberFragment implements View.OnClickListener { private static Logger log = LoggerFactory.getLogger(TreatmentsBolusFragment.class); @@ -52,6 +54,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. TextView iobTotal; TextView activityTotal; Button refreshFromNS; + Button deleteFutureTreatments; Context context; @@ -89,7 +92,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. holder.iob.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); else holder.iob.setTextColor(holder.carbs.getCurrentTextColor()); - if (t.date > DateUtil.now()) + if (t.date > now()) holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorScheduled)); else holder.date.setTextColor(holder.carbs.getCurrentTextColor()); @@ -189,6 +192,9 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. refreshFromNS = (Button) view.findViewById(R.id.treatments_reshreshfromnightscout); refreshFromNS.setOnClickListener(this); + deleteFutureTreatments = (Button) view.findViewById(R.id.treatments_delete_future_treatments); + deleteFutureTreatments.setOnClickListener(this); + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); @@ -201,17 +207,37 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. @Override public void onClick(View view) { + AlertDialog.Builder builder; switch (view.getId()) { case R.id.treatments_reshreshfromnightscout: - AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); + builder = new AlertDialog.Builder(this.getContext()); builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + "?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + TreatmentsPlugin.getPlugin().getService().resetTreatments(); + Intent restartNSClient = new Intent(Intents.ACTION_RESTART); + MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + }); + builder.setNegativeButton(MainApp.gs(R.string.cancel), null); + builder.show(); + break; + case R.id.treatments_delete_future_treatments: + builder = new AlertDialog.Builder(this.getContext()); + builder.setTitle(MainApp.gs(R.string.confirmation)); + builder.setMessage(MainApp.gs(R.string.deletefuturetreatments) + "?"); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + final List futureTreatments = TreatmentsPlugin.getPlugin().getService() + .getTreatmentDataFromTime(now() + 1000, true); + for (Treatment treatment : futureTreatments) { + final String _id = treatment._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + TreatmentsPlugin.getPlugin().getService().delete(treatment); } + updateGUI(); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -233,14 +259,16 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. protected void updateGUI() { Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); - if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { - iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " U"); - activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " U"); - } + activity.runOnUiThread(() -> { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); + if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { + iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + } + if (!TreatmentsPlugin.getPlugin().getService().getTreatmentDataFromTime(now() + 1000, true).isEmpty()) { + deleteFutureTreatments.setVisibility(View.VISIBLE); + } else { + deleteFutureTreatments.setVisibility(View.GONE); } }); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java index 97c3a05274..7e69883a3d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java @@ -107,16 +107,14 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(careportalEvent.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String _id = careportalEvent._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - MainApp.getDbHelper().delete(careportalEvent); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); } + MainApp.getDbHelper().delete(careportalEvent); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -136,12 +134,14 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)); recyclerView.setAdapter(adapter); refreshFromNS = (Button) view.findViewById(R.id.careportal_refreshfromnightscout); refreshFromNS.setOnClickListener(this); + view.findViewById(R.id.careportal_removeandroidapsstartedevents).setOnClickListener(this); + context = getContext(); boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); @@ -169,6 +169,16 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); break; + case R.id.careportal_removeandroidapsstartedevents: + builder = new AlertDialog.Builder(context); + builder.setTitle(MainApp.gs(R.string.confirmation)); + builder.setMessage(MainApp.gs(R.string.careportal_removestartedevents)); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + removeAndroidAPSStatedEvents(); + }); + builder.setNegativeButton(MainApp.gs(R.string.cancel), null); + builder.show(); + break; } } @@ -185,8 +195,24 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements activity.runOnUiThread(new Runnable() { @Override public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)), false); + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)), false); } }); } + + private void removeAndroidAPSStatedEvents() { + List events = MainApp.getDbHelper().getCareportalEvents(false); + for (int i = 0; i < events.size(); i++) { + CareportalEvent careportalEvent = events.get(i); + if (careportalEvent.json.contains(MainApp.gs(R.string.androidaps_start))) { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + MainApp.getDbHelper().delete(careportalEvent); + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java index 696b8b0b57..b5d30ad071 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java @@ -738,11 +738,14 @@ public class WatchUpdaterService extends WearableListenerService implements public static int getBatteryLevel(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); - if (level == -1 || scale == -1) { - return 50; + if (batteryIntent != null) { + int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); + if (level == -1 || scale == -1) { + return 50; + } + return (int) (((float) level / (float) scale) * 100.0f); } - return (int) (((float) level / (float) scale) * 100.0f); + return 50; } } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index 9f2fdf7eee..47fe4acd6a 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -26,6 +26,7 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressHelperAc import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.queue.commands.CommandBolus; import info.nightscout.androidaps.queue.commands.CommandCancelExtendedBolus; @@ -177,6 +178,18 @@ public class CommandQueue { public synchronized boolean bolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { Command.CommandType type = detailedBolusInfo.isSMB ? Command.CommandType.SMB_BOLUS : Command.CommandType.BOLUS; + if (type == Command.CommandType.SMB_BOLUS) { + if (isRunning(Command.CommandType.BOLUS) || bolusInQueue()) { + log.debug("Rejecting SMB since a bolus is queue/running"); + return false; + } + if (detailedBolusInfo.lastKnownBolusTime < TreatmentsPlugin.getPlugin().getLastBolusTime()) { + log.debug("Rejecting bolus, another bolus was issued since request time"); + return false; + } + } + + if(type.equals(Command.CommandType.BOLUS) && detailedBolusInfo.carbs > 0 && detailedBolusInfo.insulin == 0){ type = Command.CommandType.CARBS_ONLY_TREATMENT; //Carbs only can be added in parallel as they can be "in the future". diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java index 0e7ba0e623..a8402dc1a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java @@ -98,14 +98,7 @@ public class SWDefinition { add(new SWScreen(R.string.nav_setupwizard) .add(new SWInfotext() .label(R.string.welcometosetupwizard)) - .add(new SWButton() - .text(R.string.nav_import) - .action(() -> ImportExportPrefs.importSharedPreferences(getActivity())) - .visibility(ImportExportPrefs.file::exists)) - .add(new SWInfotext() - .label(R.string.backupismissing) - .visibility(() -> !ImportExportPrefs.file.exists())) - ) + ) .add(new SWScreen(R.string.language) .skippable(false) .add(new SWRadioButton() @@ -169,6 +162,15 @@ public class SWDefinition { .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) ) + .add(new SWScreen(R.string.nav_import) + .add(new SWInfotext() + .label(R.string.storedsettingsfound)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.nav_import) + .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) + .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) .add(new SWScreen(R.string.nsclientinternal_title) .skippable(true) .add(new SWInfotext() @@ -223,21 +225,14 @@ public class SWDefinition { ) .add(new SWScreen(R.string.configbuilder_insulin) .skippable(false) - .add(new SWInfotext() - .label(MainApp.gs(R.string.rapid_acting_oref) + ": " + MainApp.gs(R.string.fastactinginsulincomment))) - .add(new SWInfotext() - .label(MainApp.gs(R.string.ultrarapid_oref) + ": " + MainApp.gs(R.string.ultrafastactinginsulincomment))) - .add(new SWInfotext() - .label(MainApp.gs(R.string.free_peak_oref) + ": " + MainApp.gs(R.string.free_peak_oref_description))) + .add(new SWPlugin() + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin)) .add(new SWBreak()) .add(new SWInfotext() .label(R.string.diawarning)) .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.INSULIN) - .makeVisible(false) - .label(R.string.configbuilder_insulin)) - .add(new SWBreak()) .add(new SWButton() .text(R.string.insulinsourcesetup) .action(() -> { @@ -253,10 +248,8 @@ public class SWDefinition { ) .add(new SWScreen(R.string.configbuilder_bgsource) .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_bgsource_description)) .add(new SWPlugin() - .option(PluginType.BGSOURCE) + .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) .label(R.string.configbuilder_bgsource)) .add(new SWBreak()) .add(new SWButton() @@ -278,7 +271,7 @@ public class SWDefinition { .label(R.string.setupwizard_profile_description)) .add(new SWBreak()) .add(new SWPlugin() - .option(PluginType.PROFILE) + .option(PluginType.PROFILE, R.string.configbuilder_profile_description) .label(R.string.configbuilder_profile)) .validator(() -> MainApp.getConfigBuilder().getActiveProfileInterface() != null) ) @@ -324,8 +317,9 @@ public class SWDefinition { .add(new SWScreen(R.string.configbuilder_pump) .skippable(false) .add(new SWPlugin() - .option(PluginType.PUMP) + .option(PluginType.PUMP, R.string.configbuilder_pump_description) .label(R.string.configbuilder_pump)) + .add(new SWBreak()) .add(new SWButton() .text(R.string.pumpsetup) .action(() -> { @@ -360,7 +354,7 @@ public class SWDefinition { .label("https://openaps.readthedocs.io/en/latest/")) .add(new SWBreak()) .add(new SWPlugin() - .option(PluginType.APS) + .option(PluginType.APS, R.string.configbuilder_aps_description) .label(R.string.configbuilder_aps)) .add(new SWButton() .text(R.string.apssetup) @@ -402,7 +396,7 @@ public class SWDefinition { .label(R.string.setupwizard_sensitivity_url)) .add(new SWBreak()) .add(new SWPlugin() - .option(PluginType.SENSITIVITY) + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) .label(R.string.configbuilder_sensitivity)) .add(new SWBreak()) .add(new SWButton() @@ -454,13 +448,6 @@ public class SWDefinition { add(new SWScreen(R.string.nav_setupwizard) .add(new SWInfotext() .label(R.string.welcometosetupwizard)) - .add(new SWButton() - .text(R.string.nav_import) - .action(() -> ImportExportPrefs.importSharedPreferences(getActivity())) - .visibility(ImportExportPrefs.file::exists)) - .add(new SWInfotext() - .label(R.string.backupismissing) - .visibility(() -> !ImportExportPrefs.file.exists())) ) .add(new SWScreen(R.string.language) .skippable(false) @@ -513,6 +500,15 @@ public class SWDefinition { .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) ) + .add(new SWScreen(R.string.nav_import) + .add(new SWInfotext() + .label(R.string.storedsettingsfound)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.nav_import) + .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) + .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) .add(new SWScreen(R.string.nsclientinternal_title) .skippable(true) .add(new SWInfotext() @@ -567,21 +563,14 @@ public class SWDefinition { ) .add(new SWScreen(R.string.configbuilder_insulin) .skippable(false) - .add(new SWInfotext() - .label(MainApp.gs(R.string.rapid_acting_oref) + ": " + MainApp.gs(R.string.fastactinginsulincomment))) - .add(new SWInfotext() - .label(MainApp.gs(R.string.ultrarapid_oref) + ": " + MainApp.gs(R.string.ultrafastactinginsulincomment))) - .add(new SWInfotext() - .label(MainApp.gs(R.string.free_peak_oref) + ": " + MainApp.gs(R.string.free_peak_oref_description))) + .add(new SWPlugin() + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin)) .add(new SWBreak()) .add(new SWInfotext() .label(R.string.diawarning)) .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.INSULIN) - .makeVisible(false) - .label(R.string.configbuilder_insulin)) - .add(new SWBreak()) .add(new SWButton() .text(R.string.insulinsourcesetup) .action(() -> { @@ -603,7 +592,7 @@ public class SWDefinition { .label(R.string.setupwizard_sensitivity_url)) .add(new SWBreak()) .add(new SWPlugin() - .option(PluginType.SENSITIVITY) + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) .label(R.string.configbuilder_sensitivity)) .add(new SWBreak()) .add(new SWButton() diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java index 40979a3efd..487a80d91c 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java @@ -46,11 +46,11 @@ public class SWEventListener extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); textView = new TextView(context); - textView.setId(view.generateViewId()); + textView.setId(layout.generateViewId()); textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); layout.addView(textView); if (listener != null) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java index 523970d6df..5102e36ac6 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java @@ -129,7 +129,7 @@ public class SetupWizardActivity extends AppCompatActivity { LinearLayout layout = SWItem.generateLayout(this.findViewById(R.id.sw_content_fields)); for (int i = 0; i < currentScreen.items.size(); i++) { SWItem currentItem = currentScreen.items.get(i); - currentItem.generateDialog(this.findViewById(R.id.sw_content_fields), layout); + currentItem.generateDialog(layout); } scrollView.smoothScrollTo(0,0); } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java index f513cccf4e..c6a9cf3f86 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java @@ -27,8 +27,8 @@ public class SWBreak extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); l = new TextView(context); l.setId(View.generateViewId()); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java index 4f22720091..afa28c1803 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java @@ -39,8 +39,8 @@ public class SWButton extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); button = new Button(context); button.setText(buttonText); @@ -50,7 +50,7 @@ public class SWButton extends SWItem { }); processVisibility(); layout.addView(button); - super.generateDialog(view, layout); + super.generateDialog(layout); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java index 816fb79a97..ed96b37944 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java @@ -40,8 +40,8 @@ public class SWCheckbox extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); // Get if there is already value in SP Boolean previousValue; previousValue = SP.getBoolean(preferenceId, false); @@ -68,7 +68,7 @@ public class SWCheckbox extends SWItem { } }); layout.addView(checkBox); - super.generateDialog(view, layout); + super.generateDialog(layout); } public void save(boolean value){ SP.putBoolean(preferenceID, value); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java index b464ae6316..d02f10acf0 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java @@ -27,28 +27,28 @@ public class SWEditString extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); TextView l = new TextView(context); - l.setId(view.generateViewId()); + l.setId(layout.generateViewId()); l.setText(label); l.setTypeface(l.getTypeface(), Typeface.BOLD); layout.addView(l); TextView c = new TextView(context); - c.setId(view.generateViewId()); + c.setId(layout.generateViewId()); c.setText(comment); c.setTypeface(c.getTypeface(), Typeface.ITALIC); layout.addView(c); EditText editText = new EditText(context); - editText.setId(view.generateViewId()); + editText.setId(layout.generateViewId()); editText.setInputType(InputType.TYPE_CLASS_TEXT); editText.setMaxLines(1); editText.setText(SP.getString(preferenceId, "")); layout.addView(editText); - super.generateDialog(view, layout); + super.generateDialog(layout); editText.addTextChangedListener(new TextWatcher() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java index 6039577bff..04b02a5bc2 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java @@ -27,8 +27,8 @@ public class SWEditUrl extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); TextView l = new TextView(context); l.setId(View.generateViewId()); @@ -48,7 +48,7 @@ public class SWEditUrl extends SWItem { editText.setMaxLines(1); editText.setText(SP.getString(preferenceId, "")); layout.addView(editText); - super.generateDialog(view, layout); + super.generateDialog(layout); editText.addTextChangedListener(new TextWatcher() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java index 9e470fa687..a061f57eb6 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java @@ -27,7 +27,7 @@ public class SWFragment extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { + public void generateDialog(LinearLayout layout) { definition.getActivity().getSupportFragmentManager().beginTransaction().add(layout.getId(), fragment, fragment.getTag()).commit(); } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java index 1ee27be6ae..fd4ffdd524 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java @@ -39,8 +39,8 @@ public class SWHtmlLink extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); l = new TextView(context); l.setId(View.generateViewId()); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java index f98fbee82f..91e5c2da2d 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java @@ -38,8 +38,8 @@ public class SWInfotext extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); l = new TextView(context); l.setId(View.generateViewId()); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java index c82d4f5683..4f454b843d 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java @@ -78,7 +78,7 @@ public class SWItem { return layout; } - public void generateDialog(View view, LinearLayout layout) { + public void generateDialog(LinearLayout layout) { } public void processVisibility() { diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java index 06735ee72e..80f60eb678 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java @@ -2,9 +2,11 @@ package info.nightscout.androidaps.setupwizard.elements; import android.content.Context; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; +import android.widget.TextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +26,7 @@ public class SWPlugin extends SWItem { private PluginType pType; private RadioGroup radioGroup; + private int pluginDescription; private boolean makeVisible = true; @@ -31,8 +34,9 @@ public class SWPlugin extends SWItem { super(Type.PLUGIN); } - public SWPlugin option(PluginType pType) { + public SWPlugin option(PluginType pType, int pluginDescription) { this.pType = pType; + this.pluginDescription = pluginDescription; return this; } @@ -42,8 +46,9 @@ public class SWPlugin extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + + Context context = layout.getContext(); radioGroup = new RadioGroup(context); radioGroup.clearCheck(); @@ -52,6 +57,13 @@ public class SWPlugin extends SWItem { radioGroup.setOrientation(LinearLayout.VERTICAL); radioGroup.setVisibility(View.VISIBLE); + TextView pdesc = new TextView(context); + pdesc.setText(pluginDescription); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(0, 0, 0, 40); + pdesc.setLayoutParams(params); + layout.addView(pdesc); + for (int i = 0; i < pluginsInCategory.size(); i++) { RadioButton rdbtn = new RadioButton(context); PluginBase p = pluginsInCategory.get(i); @@ -61,6 +73,12 @@ public class SWPlugin extends SWItem { rdbtn.setChecked(true); rdbtn.setTag(p); radioGroup.addView(rdbtn); + params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(80, 0, 0, 0); + TextView desc = new TextView(context); + desc.setText(p.getDescription()); + desc.setLayoutParams(params); + radioGroup.addView(desc); } radioGroup.setOnCheckedChangeListener((group, checkedId) -> { @@ -74,6 +92,6 @@ public class SWPlugin extends SWItem { MainApp.bus().post(new EventSWUpdate()); }); layout.addView(radioGroup); - super.generateDialog(view, layout); + super.generateDialog(layout); } } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java index e034e86ba8..80346c433c 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java @@ -38,8 +38,8 @@ public class SWRadioButton extends SWItem { } @Override - public void generateDialog(View view, LinearLayout layout) { - Context context = view.getContext(); + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); // Get if there is already value in SP String previousValue = SP.getString(preferenceId, "none"); radioGroup = new RadioGroup(context); @@ -62,7 +62,7 @@ public class SWRadioButton extends SWItem { save(values()[i]); }); layout.addView(radioGroup); - super.generateDialog(view, layout); + super.generateDialog(layout); } public SWRadioButton preferenceId(int preferenceId) { diff --git a/app/src/main/java/info/nightscout/utils/DateUtil.java b/app/src/main/java/info/nightscout/utils/DateUtil.java index 441449e8ee..7d7db3f5a7 100644 --- a/app/src/main/java/info/nightscout/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/utils/DateUtil.java @@ -135,6 +135,11 @@ public class DateUtil { return MainApp.gs(R.string.minago, mins); } + public static String minAgoShort(long time) { + Integer mins = (int) ((time - now()) / 1000 / 60); + return (mins > 0 ? "+" : "") + mins.toString(); + } + public static String hourAgo(long time) { double hours = (now() - time) / 1000d / 60 / 60; return MainApp.gs(R.string.hoursago, hours); diff --git a/app/src/main/java/info/nightscout/utils/PercentageSplitter.java b/app/src/main/java/info/nightscout/utils/PercentageSplitter.java index 53e50466b5..40dfdf2455 100644 --- a/app/src/main/java/info/nightscout/utils/PercentageSplitter.java +++ b/app/src/main/java/info/nightscout/utils/PercentageSplitter.java @@ -8,14 +8,23 @@ import java.util.regex.Pattern; */ public class PercentageSplitter { + // "Profile name (200%,2h)" + private static final Pattern percentagePattern = Pattern.compile("(.+)\\(\\d+%,\\d+h\\)"); + // "Profile name (200%)" + private static final Pattern percentageShiftPattern = Pattern.compile("(.+)\\(\\d+%\\)"); + + /** Removes the suffix for percentage and timeshift from a profile name. */ public static String pureName(String name) { - String newName = name; - String s = "(.*)\\((\\d+)\\%\\)"; - Pattern r = Pattern.compile(s); - Matcher m = r.matcher(name); - if (m.find()) { - newName = m.group(1); + Matcher percentageMatch = percentagePattern.matcher(name); + if (percentageMatch.find()) { + return percentageMatch.group(1).trim(); } - return newName; + + Matcher percentageShiftMatch = percentageShiftPattern.matcher(name); + if (percentageShiftMatch.find()) { + return percentageShiftMatch.group(1).trim(); + } + + return name; } } diff --git a/app/src/main/java/info/nightscout/utils/StringUtils.java b/app/src/main/java/info/nightscout/utils/StringUtils.java new file mode 100644 index 0000000000..de16f7964e --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/StringUtils.java @@ -0,0 +1,20 @@ +package info.nightscout.utils; + +/** + * class contains useful String functions + */ +public class StringUtils { + + private StringUtils() { + // this constructor is private, since this class should not get instantiated + } + + public static String removeSurroundingQuotes(String string) { + if (string.length() >= 2 && string.charAt(0) == '"' + && string.charAt(string.length() - 1) == '"') { + string = string.substring(1, string.length() - 1); + } + + return string; + } +} diff --git a/app/src/main/res/layout/configbuilder_fragment.xml b/app/src/main/res/layout/configbuilder_fragment.xml index e02b30b2c8..bb5da61cfd 100644 --- a/app/src/main/res/layout/configbuilder_fragment.xml +++ b/app/src/main/res/layout/configbuilder_fragment.xml @@ -18,420 +18,10 @@ android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:padding="16dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/configbuilder_single_category.xml b/app/src/main/res/layout/configbuilder_single_category.xml new file mode 100644 index 0000000000..4c7d9787de --- /dev/null +++ b/app/src/main/res/layout/configbuilder_single_category.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/objectives_fragment.xml b/app/src/main/res/layout/objectives_fragment.xml index ea2028f5b4..8f984be622 100644 --- a/app/src/main/res/layout/objectives_fragment.xml +++ b/app/src/main/res/layout/objectives_fragment.xml @@ -1,46 +1,41 @@ - + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="16dp" + android:visibility="gone"> - + android:layout_marginRight="16dp" + android:layout_weight="1" + android:text="Enable fake time and progress" /> - - - - - - - - +