From 04ae55ac63f6f974f87b191913bfa7cad2bc19fb Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 16 Apr 2017 13:27:01 +0200 Subject: [PATCH 001/213] exit app after import settings --- .../nightscout/utils/ImportExportPrefs.java | 23 ++++++++++++++++--- app/src/main/res/values-cs/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java index 567e5e412b..0c582ba8bd 100644 --- a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java +++ b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java @@ -11,6 +11,9 @@ import android.os.Environment; import android.preference.PreferenceManager; import android.support.v4.app.ActivityCompat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -20,14 +23,17 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Map; +import info.nightscout.androidaps.MainActivity; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventAppExit; /** * Created by mike on 03.07.2016. */ public class ImportExportPrefs { + private static Logger log = LoggerFactory.getLogger(ImportExportPrefs.class); static File path = new File(Environment.getExternalStorageDirectory().toString()); static final File file = new File(path, MainApp.sResources.getString(R.string.app_name) + "Preferences"); @@ -52,7 +58,7 @@ public class ImportExportPrefs { } } - public static void exportSharedPreferences(final Context c) { + public static void exportSharedPreferences(final MainActivity c) { new AlertDialog.Builder(c) .setMessage(MainApp.sResources.getString(R.string.export_to) + " " + file + " ?") @@ -82,7 +88,7 @@ public class ImportExportPrefs { .show(); } - public static void importSharedPreferences(final Context c) { + public static void importSharedPreferences(final MainActivity c) { new AlertDialog.Builder(c) .setMessage(MainApp.sResources.getString(R.string.import_from) + " " + file + " ?") .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { @@ -109,7 +115,18 @@ public class ImportExportPrefs { } reader.close(); editor.commit(); - ToastUtils.showToastInUiThread(c, MainApp.sResources.getString(R.string.setting_imported)); + OKDialog.show(c, MainApp.sResources.getString(R.string.setting_imported), MainApp.sResources.getString(R.string.restartingapp), new Runnable() { + @Override + public void run() { + log.debug("Exiting"); + MainApp.instance().stopKeepAliveService(); + MainApp.bus().post(new EventAppExit()); + MainApp.closeDbHelper(); + c.finish(); + System.runFinalization(); + System.exit(0); + } + }); } catch (FileNotFoundException e) { ToastUtils.showToastInUiThread(c, MainApp.sResources.getString(R.string.filenotfound) + " " + file); e.printStackTrace(); diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 64eb3c63ab..9774390c24 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -532,4 +532,5 @@ K pozastavení smyčky na %d minut odpověz SMS s kódem %s Chybná doba trvání Logovat spuštění aplikace do NS + Ukončuji aplikaci, aby se nastavení projevilo. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c22459100d..ca14ce92a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -583,4 +583,5 @@ Superbolus Log app start to NS ns_logappstartedevent + Exiting application to apply settings. From 25491fbac6e4397a9ef94a4d5a9160689de72314 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 11:45:25 +0200 Subject: [PATCH 002/213] Insulin interface --- app/build.gradle | 4 +- .../info/nightscout/androidaps/Constants.java | 2 + .../info/nightscout/androidaps/MainApp.java | 2 + .../androidaps/Services/DataService.java | 10 +- .../androidaps/db/DatabaseHelper.java | 2 +- .../nightscout/androidaps/db/TempBasal.java | 6 +- .../nightscout/androidaps/db/Treatment.java | 50 ++++--- .../interfaces/InsulinInterface.java | 21 +++ .../androidaps/interfaces/PluginBase.java | 3 +- .../androidaps/interfaces/PumpInterface.java | 2 +- .../plugins/Actions/dialogs/FillDialog.java | 2 +- .../CircadianPercentageProfilePlugin.java | 2 +- .../ConfigBuilder/ConfigBuilderFragment.java | 65 ++++++--- .../ConfigBuilder/ConfigBuilderPlugin.java | 42 ++++-- .../androidaps/plugins/DanaR/DanaRPlugin.java | 5 +- .../androidaps/plugins/DanaR/DanaRPump.java | 2 +- .../DanaRKorean/DanaRKoreanPlugin.java | 5 +- .../plugins/DanaRKorean/DanaRKoreanPump.java | 2 +- .../InsulinFastactingFragment.java | 19 +++ .../InsulinFastactingPlugin.java | 124 ++++++++++++++++++ .../LocalProfile/LocalProfilePlugin.java | 2 +- .../androidaps/plugins/MDI/MDIPlugin.java | 3 +- .../Overview/Dialogs/NewTreatmentDialog.java | 2 +- .../Overview/Dialogs/WizardDialog.java | 1 + .../plugins/Overview/OverviewFragment.java | 1 + .../SimpleProfile/SimpleProfilePlugin.java | 2 +- .../SmsCommunicatorPlugin.java | 2 +- .../Treatments/TreatmentsFragment.java | 6 +- .../plugins/Treatments/TreatmentsPlugin.java | 9 +- .../VirtualPump/VirtualPumpPlugin.java | 3 +- .../plugins/Wear/ActionStringHandler.java | 4 +- app/src/main/res/drawable/insulin0.png | Bin 0 -> 29335 bytes .../res/layout/configbuilder_fragment.xml | 17 +++ app/src/main/res/values/strings.xml | 4 + 34 files changed, 338 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java create mode 100644 app/src/main/res/drawable/insulin0.png diff --git a/app/build.gradle b/app/build.gradle index 31de31b970..985ecf359c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,8 +43,8 @@ android { applicationId "info.nightscout.androidaps" minSdkVersion 21 targetSdkVersion 23 - versionCode 1100 - version "1.33" + versionCode 1400 + version "1.4" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", generateGitBuild() } diff --git a/app/src/main/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java index da0feef01f..9e5fae4aee 100644 --- a/app/src/main/java/info/nightscout/androidaps/Constants.java +++ b/app/src/main/java/info/nightscout/androidaps/Constants.java @@ -12,6 +12,8 @@ public class Constants { public static final double MMOLL_TO_MGDL = 18; // 18.0182; public static final double MGDL_TO_MMOLL = 1 / MMOLL_TO_MGDL; + public static final double defaultDIA = 3d; + public static final double basalAbsoluteOnlyForCheckLimit = 10101010d; public static final Integer basalPercentOnlyForCheckLimit = 10101010; public static final double bolusOnlyForCheckLimit = 10101010d; diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 6568e03a53..e88c2c680b 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.DanaR.DanaRFragment; import info.nightscout.androidaps.plugins.DanaRKorean.DanaRKoreanFragment; +import info.nightscout.androidaps.plugins.InsulinFastacting.InsulinFastactingFragment; import info.nightscout.androidaps.plugins.LocalProfile.LocalProfileFragment; import info.nightscout.androidaps.plugins.Loop.LoopFragment; import info.nightscout.androidaps.plugins.MDI.MDIFragment; @@ -86,6 +87,7 @@ public class MainApp extends Application { // Register all tabs in app here pluginsList.add(OverviewFragment.getPlugin()); if (Config.ACTION) pluginsList.add(ActionsFragment.getPlugin()); + pluginsList.add(InsulinFastactingFragment.getPlugin()); if (Config.DANAR) pluginsList.add(DanaRFragment.getPlugin()); if (Config.DANARKOREAN) pluginsList.add(DanaRKoreanFragment.getPlugin()); pluginsList.add(CareportalFragment.getPlugin()); 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 f45e59f4c1..2eb6a90002 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -32,10 +32,12 @@ import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.DanaR.History.DanaRNSHistorySync; +import info.nightscout.androidaps.plugins.InsulinFastacting.InsulinFastactingFragment; import info.nightscout.androidaps.plugins.NSProfile.NSProfilePlugin; import info.nightscout.androidaps.plugins.Objectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.Overview.Notification; @@ -520,7 +522,9 @@ public class DataService extends IntentService { } else { if (Config.logIncommingData) log.debug("ADD: New treatment: " + trstring); - Treatment treatment = new Treatment(); + InsulinInterface insulinInterface = MainApp.getConfigBuilder().getActiveInsulin(); + if (insulinInterface == null) insulinInterface = InsulinFastactingFragment.getPlugin(); + Treatment treatment = new Treatment(insulinInterface); treatment._id = _id; treatment.carbs = trJson.has("carbs") ? trJson.getDouble("carbs") : 0; treatment.insulin = trJson.has("insulin") ? trJson.getDouble("insulin") : 0d; @@ -575,7 +579,9 @@ public class DataService extends IntentService { if (Config.logIncommingData) log.debug("CHANGE: Adding new treatment: " + trstring); - Treatment treatment = new Treatment(); + InsulinInterface insulinInterface = MainApp.getConfigBuilder().getActiveInsulin(); + if (insulinInterface == null) insulinInterface = InsulinFastactingFragment.getPlugin(); + Treatment treatment = new Treatment(insulinInterface); treatment._id = _id; treatment.carbs = trJson.has("carbs") ? trJson.getDouble("carbs") : 0; treatment.insulin = trJson.has("insulin") ? trJson.getDouble("insulin") : 0d; 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 17ee574678..e6a5acc5bc 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -41,7 +41,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public static final String DATABASE_DANARHISTORY = "DanaRHistory"; public static final String DATABASE_DBREQUESTS = "DBRequests"; - private static final int DATABASE_VERSION = 5; + private static final int DATABASE_VERSION = 6; private long latestTreatmentChange = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java index 149d188b7c..8091000307 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java @@ -11,6 +11,7 @@ import java.util.Date; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; @@ -55,6 +56,7 @@ public class TempBasal { public IobTotal iobCalc(Date time) { IobTotal result = new IobTotal(time.getTime()); NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile(); + InsulinInterface insulinInterface = MainApp.getConfigBuilder().getActiveInsulin(); if (profile == null) return result; @@ -93,12 +95,12 @@ public class TempBasal { if (tempBolusCount > 0) { Long tempBolusSpacing = realDuration / tempBolusCount; for (Long j = 0l; j < tempBolusCount; j++) { - Treatment tempBolusPart = new Treatment(); + Treatment tempBolusPart = new Treatment(insulinInterface); tempBolusPart.insulin = tempBolusSize; Long date = this.timeStart.getTime() + j * tempBolusSpacing * 60 * 1000; tempBolusPart.created_at = new Date(date); - Iob aIOB = tempBolusPart.iobCalc(time, profile.getDia()); + Iob aIOB = insulinInterface.iobCalc(tempBolusPart, time, profile.getDia()); result.basaliob += aIOB.iobContrib; result.activity += aIOB.activityContrib; Double dia_ago = time.getTime() - profile.getDia() * 60 * 60 * 1000; diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index a8afacd767..dc933e59ee 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -11,8 +11,10 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; @@ -43,12 +45,34 @@ public class Treatment implements DataPointWithLabelInterface { @DatabaseField public Double insulin = 0d; + @DatabaseField + public int insulinType = InsulinInterface.FASTACTINGINSULIN; + + @DatabaseField + public double dia = Constants.defaultDIA; + @DatabaseField public Double carbs = 0d; @DatabaseField public boolean mealBolus = true; // true for meal bolus , false for correction bolus + public Treatment() { + InsulinInterface insulin = MainApp.getConfigBuilder().getActiveInsulin(); + if (insulin != null) { + insulinType = insulin.getId(); + dia = insulin.getDia(); + } else { + insulinType = InsulinInterface.FASTACTINGINSULIN; + dia = Constants.defaultDIA; + } + } + + public Treatment(InsulinInterface insulin) { + insulinType = insulin.getId(); + dia = insulin.getDia(); + } + public void copyFrom(Treatment t) { this._id = t._id; this.created_at = t.created_at; @@ -57,32 +81,6 @@ public class Treatment implements DataPointWithLabelInterface { this.mealBolus = t.mealBolus; } - public Iob iobCalc(Date time, Double dia) { - Iob result = new Iob(); - - Double scaleFactor = 3.0 / dia; - Double peak = 75d; - Double end = 180d; - - if (this.insulin != 0d) { - Long bolusTime = this.created_at.getTime(); - Double minAgo = scaleFactor * (time.getTime() - bolusTime) / 1000d / 60d; - - if (minAgo < peak) { - Double x1 = minAgo / 5d + 1; - result.iobContrib = this.insulin * (1 - 0.001852 * x1 * x1 + 0.001852 * x1); - // units: BG (mg/dL) = (BG/U) * U insulin * scalar - result.activityContrib = this.insulin * (2 / dia / 60 / peak) * minAgo; - - } else if (minAgo < end) { - Double x2 = (minAgo - 75) / 5; - result.iobContrib = this.insulin * (0.001323 * x2 * x2 - 0.054233 * x2 + 0.55556); - result.activityContrib = this.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * 3 - peak)); - } - } - return result; - } - public long getMillisecondsFromStart() { return new Date().getTime() - created_at.getTime(); } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java new file mode 100644 index 0000000000..05261e2916 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.interfaces; + +import java.util.Date; + +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; + +/** + * Created by mike on 17.04.2017. + */ + +public interface InsulinInterface { + final int FASTACTINGINSULIN = 0; + + int getId(); + String getFriendlyName(); + String getComment(); + int getResourcePicture(); + double getDia(); + public Iob iobCalc(Treatment treatment, Date time, Double dia); +} 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 4442b83041..357737aa65 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -15,7 +15,8 @@ public interface PluginBase { int CONSTRAINTS = 7; int LOOP = 8; int BGSOURCE = 9; - int LAST = 10; // keep always highest number + int INSULIN = 10; + int LAST = 11; // keep always highest number int getType(); String getFragmentClass(); diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java index 6b6d8e856f..b9c7e18650 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java @@ -39,7 +39,7 @@ public interface PumpInterface { TempBasal getTempBasal(); TempBasal getExtendedBolus(); - PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context); + PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context); void stopBolusDelivering(); PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes); PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java index 8f50ac28e0..dd623c38e9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java @@ -159,7 +159,7 @@ public class FillDialog extends DialogFragment implements OnClickListener { mHandler.post(new Runnable() { @Override public void run() { - PumpEnactResult result = pump.deliverTreatment(finalInsulinAfterConstraints, 0, context, false); + PumpEnactResult result = pump.deliverTreatment(MainApp.getConfigBuilder().getActiveInsulin(), finalInsulinAfterConstraints, 0, context, false); if (!result.success) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.sResources.getString(R.string.treatmentdeliveryerror)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java index 201b2ac37b..b7032edb5d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java @@ -129,7 +129,7 @@ public class CircadianPercentageProfilePlugin implements PluginBase, ProfileInte mgdl = SP.getBoolean(SETTINGS_PREFIX + "mgdl", true); mmol = SP.getBoolean(SETTINGS_PREFIX + "mmol", false); - dia = SP.getDouble(SETTINGS_PREFIX + "dia", 3d); + dia = SP.getDouble(SETTINGS_PREFIX + "dia", Constants.defaultDIA); targetLow = SP.getDouble(SETTINGS_PREFIX + "targetlow", 80d); targetHigh = SP.getDouble(SETTINGS_PREFIX + "targethigh", 120d); percentage = SP.getInt(SETTINGS_PREFIX + "percentage", 100); 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 6436441890..0565cac925 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 @@ -21,6 +21,9 @@ import com.crashlytics.android.answers.CustomEvent; import java.util.ArrayList; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventConfigBuilderChange; @@ -29,9 +32,11 @@ import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.FragmentBase; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.InsulinFastacting.InsulinFastactingPlugin; import info.nightscout.androidaps.plugins.NSProfile.NSProfilePlugin; import info.nightscout.androidaps.plugins.VirtualPump.VirtualPumpPlugin; import info.nightscout.utils.PasswordProtection; @@ -45,23 +50,44 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { return configBuilderPlugin; } + @BindView(R.id.configbuilder_insulinlistview) + ListView insulinListView; + @BindView(R.id.configbuilder_bgsourcelistview) ListView bgsourceListView; + @BindView(R.id.configbuilder_pumplistview) ListView pumpListView; + @BindView(R.id.configbuilder_pumplabel) TextView pumpLabel; + @BindView(R.id.configbuilder_looplistview) ListView loopListView; + @BindView(R.id.configbuilder_looplabel) TextView loopLabel; + @BindView(R.id.configbuilder_treatmentslistview) ListView treatmentsListView; + @BindView(R.id.configbuilder_tempslistview) ListView tempsListView; + @BindView(R.id.configbuilder_tempslabel) TextView tempsLabel; + @BindView(R.id.configbuilder_profilelistview) ListView profileListView; + @BindView(R.id.configbuilder_apslistview) ListView apsListView; + @BindView(R.id.configbuilder_apslabel) TextView apsLabel; + @BindView(R.id.configbuilder_constraintslistview) ListView constraintsListView; + @BindView(R.id.configbuilder_constraintslabel) TextView constraintsLabel; + @BindView(R.id.configbuilder_generallistview) ListView generalListView; + @BindView(R.id.configbuilder_nsclientversion) TextView nsclientVerView; + @BindView(R.id.configbuilder_nightscoutversion) TextView nightscoutVerView; + private Unbinder unbinder; + + PluginCustomAdapter insulinDataAdapter = null; PluginCustomAdapter bgsourceDataAdapter = null; PluginCustomAdapter pumpDataAdapter = null; PluginCustomAdapter loopDataAdapter = null; @@ -72,7 +98,9 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { PluginCustomAdapter constraintsDataAdapter = null; PluginCustomAdapter generalDataAdapter = null; + @BindView(R.id.configbuilder_mainlayout) LinearLayout mainLayout; + @BindView(R.id.configbuilder_unlock) Button unlock; // TODO: sorting @@ -81,22 +109,8 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.configbuilder_fragment, container, false); - bgsourceListView = (ListView) view.findViewById(R.id.configbuilder_bgsourcelistview); - pumpListView = (ListView) view.findViewById(R.id.configbuilder_pumplistview); - pumpLabel = (TextView) view.findViewById(R.id.configbuilder_pumplabel); - loopListView = (ListView) view.findViewById(R.id.configbuilder_looplistview); - loopLabel = (TextView) view.findViewById(R.id.configbuilder_looplabel); - treatmentsListView = (ListView) view.findViewById(R.id.configbuilder_treatmentslistview); - tempsListView = (ListView) view.findViewById(R.id.configbuilder_tempslistview); - tempsLabel = (TextView) view.findViewById(R.id.configbuilder_tempslabel); - profileListView = (ListView) view.findViewById(R.id.configbuilder_profilelistview); - apsListView = (ListView) view.findViewById(R.id.configbuilder_apslistview); - apsLabel = (TextView) view.findViewById(R.id.configbuilder_apslabel); - constraintsListView = (ListView) view.findViewById(R.id.configbuilder_constraintslistview); - constraintsLabel = (TextView) view.findViewById(R.id.configbuilder_constraintslabel); - generalListView = (ListView) view.findViewById(R.id.configbuilder_generallistview); - nsclientVerView = (TextView) view.findViewById(R.id.configbuilder_nsclientversion); - nightscoutVerView = (TextView) view.findViewById(R.id.configbuilder_nightscoutversion); + + unbinder = ButterKnife.bind(this, view); nsclientVerView.setText(ConfigBuilderPlugin.nsClientVersionName); nightscoutVerView.setText(ConfigBuilderPlugin.nightscoutVersionName); @@ -105,9 +119,6 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { nightscoutVerView.setTextColor(Color.RED); setViews(); - unlock = (Button) view.findViewById(R.id.configbuilder_unlock); - mainLayout = (LinearLayout) view.findViewById(R.id.configbuilder_mainlayout); - if (PasswordProtection.isLocked("settings_password")) { mainLayout.setVisibility(View.GONE); unlock.setOnClickListener(new View.OnClickListener() { @@ -128,7 +139,16 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { return view; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + void setViews() { + insulinDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsListByInterface(InsulinInterface.class), PluginBase.INSULIN); + insulinListView.setAdapter(insulinDataAdapter); + setListViewHeightBasedOnChildren(insulinListView); bgsourceDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsListByInterface(BgSourceInterface.class), PluginBase.BGSOURCE); bgsourceListView.setAdapter(bgsourceDataAdapter); setListViewHeightBasedOnChildren(bgsourceListView); @@ -253,7 +273,7 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { } // Hide enabled control and force enabled plugin if there is only one plugin available - if (type == PluginBase.PUMP || type == PluginBase.TREATMENT || type == PluginBase.TEMPBASAL || type == PluginBase.PROFILE) + if (type == PluginBase.INSULIN || type == PluginBase.PUMP || type == PluginBase.TREATMENT || type == PluginBase.TEMPBASAL || type == PluginBase.PROFILE) if (pluginList.size() < 2) { holder.checkboxEnabled.setEnabled(false); plugin.setFragmentEnabled(type, true); @@ -299,6 +319,9 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { case PluginBase.LOOP: break; // Single selection allowed + case PluginBase.INSULIN: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(InsulinInterface.class); + break; case PluginBase.APS: pluginsInCategory = MainApp.getSpecificPluginsListByInterface(APSInterface.class); break; @@ -328,6 +351,8 @@ public class ConfigBuilderFragment extends Fragment implements FragmentBase { } else { // enable first plugin in list if (type == PluginBase.PUMP) MainApp.getSpecificPlugin(VirtualPumpPlugin.class).setFragmentEnabled(type, true); + else if (type == PluginBase.INSULIN) + MainApp.getSpecificPlugin(InsulinFastactingPlugin.class).setFragmentEnabled(type, true); else if (type == PluginBase.PROFILE) MainApp.getSpecificPlugin(NSProfilePlugin.class).setFragmentEnabled(type, true); else 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 2c42ffaa41..30baa566eb 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 @@ -37,6 +37,7 @@ import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -74,6 +75,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain static TempBasalsInterface activeTempBasals; static APSInterface activeAPS; static LoopPlugin activeLoop; + static InsulinInterface activeInsulin; static public String nightscoutVersionName = ""; static public Integer nightscoutVersionCode = 0; @@ -205,6 +207,10 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return activeTempBasals; } + public static InsulinInterface getActiveInsulin() { + return activeInsulin; + } + public static APSInterface getActiveAPS() { return activeAPS; } @@ -224,7 +230,8 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain (p.isEnabled(6) ? " PUMP" : "") + (p.isEnabled(7) ? " CONSTRAINTS" : "") + (p.isEnabled(8) ? " LOOP" : "") + - (p.isEnabled(9) ? " BGSOURCE" : "") + (p.isEnabled(9) ? " BGSOURCE" : "") + + (p.isEnabled(10) ? " INSULIN" : "") ); } } @@ -245,7 +252,18 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } } - // PluginBase.PROFILE + // PluginBase.INSULIN + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(InsulinInterface.class); + activeInsulin = (InsulinInterface) getTheOneEnabledInArray(pluginsInCategory, PluginBase.INSULIN); + if (Config.logConfigBuilder) + log.debug("Selected insulin interface: " + ((PluginBase) activeInsulin).getName()); + for (PluginBase p : pluginsInCategory) { + if (!p.getName().equals(((PluginBase) activeInsulin).getName())) { + p.setFragmentVisible(PluginBase.INSULIN, false); + } + } + + // PluginBase.PROFILE pluginsInCategory = MainApp.getSpecificPluginsListByInterface(ProfileInterface.class); activeProfile = (ProfileInterface) getTheOneEnabledInArray(pluginsInCategory, PluginBase.PROFILE); if (Config.logConfigBuilder) @@ -463,7 +481,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return null; } - public PumpEnactResult deliverTreatmentFromBolusWizard(Context context, Double insulin, Integer carbs, Double glucose, String glucoseType, int carbTime, JSONObject boluscalc) { + public PumpEnactResult deliverTreatmentFromBolusWizard(InsulinInterface insulinType, Context context, Double insulin, Integer carbs, Double glucose, String glucoseType, int carbTime, JSONObject boluscalc) { mWakeLock.acquire(); PumpEnactResult result; if (activePump != null) { @@ -479,14 +497,14 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain MainApp.bus().post(new EventBolusRequested(insulin)); - result = activePump.deliverTreatment(insulin, carbs, context); + result = activePump.deliverTreatment(insulinType, insulin, carbs, context); BolusProgressDialog.bolusEnded = true; MainApp.bus().post(new EventDismissBolusprogressIfRunning(result)); if (result.success) { - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); t.insulin = result.bolusDelivered; if (carbTime == 0) t.carbs = (double) result.carbsDelivered; // with different carbTime record will come back from nightscout @@ -500,7 +518,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } else { if (Config.logCongigBuilderActions) log.debug("Creating treatment: " + insulin + " carbs: " + carbs); - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); t.insulin = insulin; t.carbs = (double) carbs; t.created_at = new Date(); @@ -518,11 +536,11 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } @Override - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { - return deliverTreatment(insulin, carbs, context, true); + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) { + return deliverTreatment(insulinType, insulin, carbs, context, true); } - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context, boolean createTreatment) { + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context, boolean createTreatment) { mWakeLock.acquire(); PumpEnactResult result; if (activePump != null) { @@ -544,7 +562,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain MainApp.bus().post(new EventBolusRequested(insulin)); - result = activePump.deliverTreatment(insulin, carbs, context); + result = activePump.deliverTreatment(insulinType, insulin, carbs, context); BolusProgressDialog.bolusEnded = true; @@ -554,7 +572,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain log.debug("deliverTreatment insulin: " + insulin + " carbs: " + carbs + " success: " + result.success + " enacted: " + result.enacted + " bolusDelivered: " + result.bolusDelivered); if (result.success && createTreatment) { - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); t.insulin = result.bolusDelivered; t.carbs = (double) result.carbsDelivered; t.created_at = new Date(); @@ -566,7 +584,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } else { if (Config.logCongigBuilderActions) log.debug("Creating treatment: " + insulin + " carbs: " + carbs); - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); t.insulin = insulin; t.carbs = (double) carbs; t.created_at = new Date(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPlugin.java index 4a2d9d3a56..2984980090 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPlugin.java @@ -30,6 +30,7 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -357,11 +358,11 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf } @Override - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) { ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); if (insulin > 0 || carbs > 0) { - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); boolean connectionOK = false; if (insulin > 0 || carbs > 0) connectionOK = sExecutionService.bolus(insulin, carbs, t); PumpEnactResult result = new PumpEnactResult(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPump.java index c68bac86ac..65e65b11a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/DanaR/DanaRPump.java @@ -119,7 +119,7 @@ public class DanaRPump { // Evening / 17:00–21:59 // Night / 22:00–5:59 - double dia = SP.getDouble(R.string.key_danarprofile_dia, 3d); + double dia = SP.getDouble(R.string.key_danarprofile_dia, Constants.defaultDIA); try { json.put("defaultProfile", PROFILE_PREFIX + (activeProfile + 1)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPlugin.java index 11debc415a..f32cd1a8a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPlugin.java @@ -30,6 +30,7 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -358,11 +359,11 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints } @Override - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) { ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); if (insulin > 0 || carbs > 0) { - Treatment t = new Treatment(); + Treatment t = new Treatment(insulinType); boolean connectionOK = false; if (insulin > 0 || carbs > 0) connectionOK = sExecutionService.bolus(insulin, carbs, t); PumpEnactResult result = new PumpEnactResult(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPump.java index 6786cc4c2e..b768c1c2ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/DanaRKorean/DanaRKoreanPump.java @@ -114,7 +114,7 @@ public class DanaRKoreanPump { // Evening / 17:00–21:59 // Night / 22:00–5:59 - double dia = SP.getDouble(R.string.key_danarprofile_dia, 3d); + double dia = SP.getDouble(R.string.key_danarprofile_dia, Constants.defaultDIA); try { json.put("defaultProfile", PROFILE_PREFIX + (activeProfile + 1)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java new file mode 100644 index 0000000000..bff8436861 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.InsulinFastacting; + +import android.support.v4.app.Fragment; + +import info.nightscout.androidaps.interfaces.FragmentBase; + +/** + * Created by mike on 17.04.2017. + */ + +public class InsulinFastactingFragment extends Fragment implements FragmentBase { + static InsulinFastactingPlugin insulinFastactingPlugin = new InsulinFastactingPlugin(); + + static public InsulinFastactingPlugin getPlugin() { + return insulinFastactingPlugin; + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java new file mode 100644 index 0000000000..a43bccf927 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java @@ -0,0 +1,124 @@ +package info.nightscout.androidaps.plugins.InsulinFastacting; + +import java.util.Date; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.interfaces.InsulinInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.ProfileInterface; + +/** + * Created by mike on 17.04.2017. + */ + +public class InsulinFastactingPlugin implements PluginBase, InsulinInterface { + + private static boolean fragmentEnabled = true; + private static boolean fragmentVisible = false; + + @Override + public int getType() { + return INSULIN; + } + + @Override + public String getFragmentClass() { + return InsulinFastactingFragment.class.getName(); + } + + @Override + public String getName() { + return MainApp.sResources.getString(R.string.fastactinginsulin); + } + + @Override + public String getNameShort() { + return MainApp.sResources.getString(R.string.insulin_shortname); + } + + @Override + public boolean isEnabled(int type) { + return type == INSULIN && fragmentEnabled; + } + + @Override + public boolean isVisibleInTabs(int type) { + return type == INSULIN && fragmentVisible; + } + + @Override + public boolean canBeHidden(int type) { + return true; + } + + @Override + public void setFragmentEnabled(int type, boolean fragmentEnabled) { + if (type == INSULIN) this.fragmentEnabled = fragmentEnabled; + } + + @Override + public void setFragmentVisible(int type, boolean fragmentVisible) { + if (type == INSULIN) this.fragmentVisible = fragmentVisible; + } + + // Insulin interface + @Override + public int getId() { + return FASTACTINGINSULIN; + } + + @Override + public String getFriendlyName() { + return MainApp.sResources.getString(R.string.fastactinginsulin); + } + + @Override + public String getComment() { + return MainApp.sResources.getString(R.string.fastactinginsulincomment); + } + + @Override + public int getResourcePicture() { + return R.drawable.insulin0; + } + + @Override + public double getDia() { + ProfileInterface profileInterface = MainApp.getConfigBuilder().getActiveProfile(); + if (profileInterface.getProfile() != null) + return profileInterface.getProfile().getDia(); + return Constants.defaultDIA; + } + + @Override + public Iob iobCalc(Treatment treatment, Date time, Double dia) { + Iob result = new Iob(); + + Double scaleFactor = 3.0 / dia; + Double peak = 75d; + Double end = 180d; + + if (treatment.insulin != 0d) { + Long bolusTime = treatment.created_at.getTime(); + Double minAgo = scaleFactor * (time.getTime() - bolusTime) / 1000d / 60d; + + if (minAgo < peak) { + Double x1 = minAgo / 5d + 1; + result.iobContrib = treatment.insulin * (1 - 0.001852 * x1 * x1 + 0.001852 * x1); + // units: BG (mg/dL) = (BG/U) * U insulin * scalar + result.activityContrib = treatment.insulin * (2 / dia / 60 / peak) * minAgo; + + } else if (minAgo < end) { + Double x2 = (minAgo - 75) / 5; + result.iobContrib = treatment.insulin * (0.001323 * x2 * x2 - 0.054233 * x2 + 0.55556); + result.activityContrib = treatment.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * 3 - peak)); + } + } + return result; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/LocalProfile/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/LocalProfile/LocalProfilePlugin.java index a4c65e83dd..44d2ba9397 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/LocalProfile/LocalProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/LocalProfile/LocalProfilePlugin.java @@ -120,7 +120,7 @@ public class LocalProfilePlugin implements PluginBase, ProfileInterface { mgdl = SP.getBoolean("LocalProfile" + "mgdl", false); mmol = SP.getBoolean("LocalProfile" + "mmol", true); - dia = SP.getDouble("LocalProfile" + "dia", 3d); + dia = SP.getDouble("LocalProfile" + "dia", Constants.defaultDIA); try { ic = new JSONArray(SP.getString("LocalProfile" + "ic", DEFAULTARRAY)); } catch (JSONException e1) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/MDI/MDIPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/MDI/MDIPlugin.java index 534a77b766..821249ea83 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/MDI/MDIPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/MDI/MDIPlugin.java @@ -15,6 +15,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.TempBasal; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; @@ -183,7 +184,7 @@ public class MDIPlugin implements PluginBase, PumpInterface { } @Override - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) { PumpEnactResult result = new PumpEnactResult(); result.success = true; result.bolusDelivered = insulin; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java index ec25d2a0b0..e97104437d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java @@ -110,7 +110,7 @@ public class NewTreatmentDialog extends DialogFragment implements OnClickListene mHandler.post(new Runnable() { @Override public void run() { - PumpEnactResult result = pump.deliverTreatment(finalInsulinAfterConstraints, finalCarbsAfterConstraints, context); + PumpEnactResult result = pump.deliverTreatment(MainApp.getConfigBuilder().getActiveInsulin(), finalInsulinAfterConstraints, finalCarbsAfterConstraints, context); if (!result.success) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.sResources.getString(R.string.treatmentdeliveryerror)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java index eaebeec73e..20c9c0a0db 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java @@ -314,6 +314,7 @@ public class WizardDialog extends DialogFragment { @Override public void run() { PumpEnactResult result = pump.deliverTreatmentFromBolusWizard( + MainApp.getConfigBuilder().getActiveInsulin(), context, finalInsulinAfterConstraints, finalCarbsAfterConstraints, 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 22d5e48b57..b5c7ee298a 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 @@ -510,6 +510,7 @@ public class OverviewFragment extends Fragment { @Override public void run() { PumpEnactResult result = pump.deliverTreatmentFromBolusWizard( + MainApp.getConfigBuilder().getActiveInsulin(), getContext(), finalInsulinAfterConstraints, finalCarbsAfterConstraints, diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SimpleProfile/SimpleProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SimpleProfile/SimpleProfilePlugin.java index f40d85371e..27548a2bd7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SimpleProfile/SimpleProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SimpleProfile/SimpleProfilePlugin.java @@ -118,7 +118,7 @@ public class SimpleProfilePlugin implements PluginBase, ProfileInterface { mgdl = SP.getBoolean("SimpleProfile" + "mgdl", true); mmol = SP.getBoolean("SimpleProfile" + "mmol", false); - dia = SP.getDouble("SimpleProfile" + "dia", 3d); + dia = SP.getDouble("SimpleProfile" + "dia", Constants.defaultDIA); ic = SP.getDouble("SimpleProfile" + "ic", 20d); isf = SP.getDouble("SimpleProfile" + "isf", 200d); basal = SP.getDouble("SimpleProfile" + "basal", 1d); 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 fcaf379af2..e9fcd3b0f6 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 @@ -442,7 +442,7 @@ public class SmsCommunicatorPlugin implements PluginBase { PumpInterface pumpInterface = MainApp.getConfigBuilder(); if (pumpInterface != null) { danaRPlugin = (DanaRPlugin) MainApp.getSpecificPlugin(DanaRPlugin.class); - PumpEnactResult result = pumpInterface.deliverTreatment(bolusWaitingForConfirmation.bolusRequested, 0, null); + PumpEnactResult result = pumpInterface.deliverTreatment(MainApp.getConfigBuilder().getActiveInsulin() ,bolusWaitingForConfirmation.bolusRequested, 0, null); if (result.success) { reply = String.format(MainApp.sResources.getString(R.string.smscommunicator_bolusdelivered), result.bolusDelivered); if (danaRPlugin != null) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index 320aba3956..f1539c4e4c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -38,6 +38,7 @@ import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.FragmentBase; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; @@ -81,12 +82,13 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener if (MainApp.getConfigBuilder() == null || MainApp.getConfigBuilder().getActiveProfile() == null) // app not initialized yet return; NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile(); - if (profile == null) + InsulinInterface insulinInterface = MainApp.getConfigBuilder().getActiveInsulin(); + if (profile == null || insulinInterface == null) return; holder.date.setText(DateUtil.dateAndTimeString(treatments.get(position).created_at)); holder.insulin.setText(DecimalFormatter.to2Decimal(treatments.get(position).insulin) + " U"); holder.carbs.setText(DecimalFormatter.to0Decimal(treatments.get(position).carbs) + " g"); - Iob iob = treatments.get(position).iobCalc(new Date(), profile.getDia()); + Iob iob = insulinInterface.iobCalc(treatments.get(position), new Date(), profile.getDia()); holder.iob.setText(DecimalFormatter.to2Decimal(iob.iobContrib) + " U"); holder.activity.setText(DecimalFormatter.to3Decimal(iob.activityContrib) + " U"); holder.mealOrCorrection.setText(treatments.get(position).mealBolus ? MainApp.sResources.getString(R.string.mealbolus) : MainApp.sResources.getString(R.string.correctionbous)); 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 c49e26553e..9f1bed187f 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 @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Iob; @@ -15,6 +16,7 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventTreatmentChange; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; @@ -92,7 +94,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { } public void initializeData() { - double dia = 3; + double dia = Constants.defaultDIA; if (MainApp.getConfigBuilder().getActiveProfile() != null && MainApp.getConfigBuilder().getActiveProfile().getProfile() != null) dia = MainApp.getConfigBuilder().getActiveProfile().getProfile().getDia(); long fromMills = (long) (new Date().getTime() - 60 * 60 * 1000L * (24 + dia)); @@ -120,6 +122,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { if (MainApp.getConfigBuilder() == null || ConfigBuilderPlugin.getActiveProfile() == null) // app not initialized yet return total; NSProfile profile = ConfigBuilderPlugin.getActiveProfile().getProfile(); + InsulinInterface insulinInterface = MainApp.getConfigBuilder().getActiveInsulin(); if (profile == null) return total; @@ -129,10 +132,10 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { for (Integer pos = 0; pos < treatments.size(); pos++) { Treatment t = treatments.get(pos); if (t.created_at.getTime() > time) continue; - Iob tIOB = t.iobCalc(now, dia); + Iob tIOB = insulinInterface.iobCalc(t, now, dia); total.iob += tIOB.iobContrib; total.activity += tIOB.activityContrib; - Iob bIOB = t.iobCalc(now, dia / SP.getInt("openapsama_bolussnooze_dia_divisor", 2)); + Iob bIOB = insulinInterface.iobCalc(t, now, dia / SP.getInt("openapsama_bolussnooze_dia_divisor", 2)); total.bolussnooze += bIOB.iobContrib; } return total; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/VirtualPump/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/VirtualPump/VirtualPumpPlugin.java index 2e3a802a7a..f99b237eab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/VirtualPump/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/VirtualPump/VirtualPumpPlugin.java @@ -18,6 +18,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.TempBasal; +import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; @@ -219,7 +220,7 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { } @Override - public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { + public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) { PumpEnactResult result = new PumpEnactResult(); result.success = true; result.bolusDelivered = insulin; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java index ba93968e78..682073d42f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java @@ -384,7 +384,7 @@ public class ActionStringHandler { handler.post(new Runnable() { @Override public void run() { - PumpEnactResult result = MainApp.getConfigBuilder().deliverTreatment(amount, 0, null, false); + PumpEnactResult result = MainApp.getConfigBuilder().deliverTreatment(MainApp.getConfigBuilder().getActiveInsulin(), amount, 0, null, false); if (!result.success) { sendError(MainApp.sResources.getString(R.string.treatmentdeliveryerror) + "\n" + @@ -400,7 +400,7 @@ public class ActionStringHandler { handler.post(new Runnable() { @Override public void run() { - PumpEnactResult result = MainApp.getConfigBuilder().deliverTreatment(amount, carbs, null, true); + PumpEnactResult result = MainApp.getConfigBuilder().deliverTreatment(MainApp.getConfigBuilder().getActiveInsulin(), amount, carbs, null, true); if (!result.success) { sendError(MainApp.sResources.getString(R.string.treatmentdeliveryerror) + "\n" + diff --git a/app/src/main/res/drawable/insulin0.png b/app/src/main/res/drawable/insulin0.png new file mode 100644 index 0000000000000000000000000000000000000000..f0cbe937f8e96696f36f9398c00ef07c7eb5a73a GIT binary patch literal 29335 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 + + + + + Log app start to NS ns_logappstartedevent Exiting application to apply settings. + Insulin + Fast Acting Insulin + Novorapid, Novolog, Humalog + INS From 51439bbf33fa7fc66fcd452aa18716c543359a55 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 12:03:54 +0200 Subject: [PATCH 003/213] upgrade libraries --- app/build.gradle | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 985ecf359c..0d9ffed984 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,13 +36,13 @@ def generateGitBuild = { -> } android { - compileSdkVersion 23 + compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "info.nightscout.androidaps" minSdkVersion 21 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1400 version "1.4" buildConfigField "String", "VERSION", '"' + version + '"' @@ -147,13 +147,13 @@ dependencies { transitive = true; } - compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.android.support:support-v4:23.4.0' - compile 'com.android.support:cardview-v7:23.4.0' - compile 'com.android.support:recyclerview-v7:23.4.0' - compile 'com.android.support:gridlayout-v7:23.4.0' - compile "com.android.support:design:23.4.0" - compile "com.android.support:percent:23.4.0" + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:support-v4:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.android.support:gridlayout-v7:25.3.1' + compile "com.android.support:design:25.3.1" + compile "com.android.support:percent:25.3.1" compile 'com.wdullaer:materialdatetimepicker:2.3.0' compile 'com.squareup:otto:1.3.7' compile 'com.j256.ormlite:ormlite-core:4.46' @@ -168,8 +168,8 @@ dependencies { compile 'com.google.android.gms:play-services-wearable:7.5.0' compile 'junit:junit:4.12' testCompile 'org.json:json:20140107' - testCompile 'org.mockito:mockito-core:2.+' - androidTestCompile 'org.mockito:mockito-core:2.+' + testCompile 'org.mockito:mockito-core:2.7.22' + androidTestCompile 'org.mockito:mockito-core:2.7.22' androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' compile(name: 'android-edittext-validator-v1.3.4-mod', ext: 'aar') @@ -177,8 +177,8 @@ dependencies { // excluding org.json which is provided by Android exclude group: 'org.json', module: 'json' } - compile 'com.google.code.gson:gson:2.4' - compile 'com.google.guava:guava:18.0' + compile 'com.google.code.gson:gson:2.7' + compile 'com.google.guava:guava:20.0' compile ('com.jakewharton:butterknife:8.5.1') { exclude module: 'support-compat' From 17771434aa7b99830f14a8183b66c336af3531b4 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 12:15:08 +0200 Subject: [PATCH 004/213] upgrade travis & plugin packages --- .travis.yml | 4 +- app/src/main/AndroidManifest.xml | 12 +-- .../info/nightscout/androidaps/MainApp.java | 24 ++--- .../androidaps/PreferencesActivity.java | 8 +- .../androidaps/Services/DataService.java | 6 +- .../Dialogs/NewNSTreatmentDialog.java | 2 +- .../ConfigBuilder/ConfigBuilderFragment.java | 4 +- .../ConfigBuilder/ConfigBuilderPlugin.java | 9 +- .../ObjectivesFragment.java | 3 +- .../ObjectivesPlugin.java | 3 +- .../SafetyFragment.java | 2 +- .../SafetyPlugin.java | 2 +- .../androidaps/plugins/MDI/MDIFragment.java | 33 ------- .../NSClientInternalPlugin.java | 7 -- .../services/NSClientService.java | 4 +- .../Overview/Dialogs/BolusProgressDialog.java | 2 +- .../plugins/Overview/OverviewFragment.java | 2 +- .../CircadianPercentageProfileFragment.java | 2 +- .../CircadianPercentageProfilePlugin.java | 2 +- .../LocalProfileFragment.java | 3 +- .../LocalProfilePlugin.java | 3 +- .../NSProfileFragment.java | 4 +- .../NSProfilePlugin.java | 4 +- .../events/EventNSProfileUpdateGUI.java | 2 +- .../SimpleProfileFragment.java | 9 +- .../SimpleProfilePlugin.java | 3 +- .../BluetoothDevicePreference.java | 2 +- .../{DanaR => PumpDanaR}/DanaRFragment.java | 10 +- .../{DanaR => PumpDanaR}/DanaRPlugin.java | 6 +- .../{DanaR => PumpDanaR}/DanaRPump.java | 2 +- .../Dialogs/ProfileViewDialog.java | 4 +- .../History/DanaRHistoryActivity.java | 8 +- .../History/DanaRNSHistorySync.java | 6 +- .../History/DanaRStatsActivity.java | 10 +- .../{DanaR => PumpDanaR}/SerialIOThread.java | 6 +- .../Services/ExecutionService.java | 93 +++++++++---------- .../comm/MessageBase.java | 2 +- .../comm/MessageHashTable.java | 2 +- .../comm/MessageOriginalNames.java | 2 +- .../comm/MsgBolusProgress.java | 4 +- .../comm/MsgBolusStart.java | 3 +- .../comm/MsgBolusStop.java | 4 +- .../comm/MsgCheckValue.java | 6 +- .../{DanaR => PumpDanaR}/comm/MsgError.java | 2 +- .../comm/MsgHistoryAlarm.java | 2 +- .../comm/MsgHistoryAll.java | 4 +- .../comm/MsgHistoryAllDone.java | 2 +- .../comm/MsgHistoryBasalHour.java | 2 +- .../comm/MsgHistoryBolus.java | 2 +- .../comm/MsgHistoryCarbo.java | 2 +- .../comm/MsgHistoryDailyInsulin.java | 2 +- .../comm/MsgHistoryDone.java | 2 +- .../comm/MsgHistoryError.java | 2 +- .../comm/MsgHistoryGlucose.java | 2 +- .../comm/MsgHistoryNew.java | 2 +- .../comm/MsgHistoryNewDone.java | 2 +- .../comm/MsgHistoryRefill.java | 2 +- .../comm/MsgHistorySuspend.java | 2 +- .../comm/MsgInitConnStatusBasic.java | 6 +- .../comm/MsgInitConnStatusBolus.java | 6 +- .../comm/MsgInitConnStatusOption.java | 4 +- .../comm/MsgInitConnStatusTime.java | 7 +- .../comm/MsgPCCommStart.java | 2 +- .../comm/MsgPCCommStop.java | 2 +- .../comm/MsgSetActivateBasalProfile.java | 2 +- .../comm/MsgSetBasalProfile.java | 2 +- .../comm/MsgSetCarbsEntry.java | 2 +- .../comm/MsgSetExtendedBolusStart.java | 3 +- .../comm/MsgSetExtendedBolusStop.java | 2 +- .../comm/MsgSetSingleBasalProfile.java | 4 +- .../comm/MsgSetTempBasalStart.java | 2 +- .../comm/MsgSetTempBasalStop.java | 2 +- .../{DanaR => PumpDanaR}/comm/MsgSetTime.java | 2 +- .../comm/MsgSettingActiveProfile.java | 4 +- .../comm/MsgSettingBasal.java | 6 +- .../comm/MsgSettingBasalProfileAll.java | 6 +- .../comm/MsgSettingGlucose.java | 6 +- .../comm/MsgSettingMaxValues.java | 4 +- .../comm/MsgSettingMeal.java | 6 +- .../comm/MsgSettingProfileRatios.java | 6 +- .../comm/MsgSettingProfileRatiosAll.java | 6 +- .../comm/MsgSettingPumpTime.java | 4 +- .../comm/MsgSettingShippingInfo.java | 8 +- .../comm/MsgSettingUserOptions.java | 2 +- .../{DanaR => PumpDanaR}/comm/MsgStatus.java | 4 +- .../comm/MsgStatusBasic.java | 6 +- .../comm/MsgStatusBolusExtended.java | 6 +- .../comm/MsgStatusProfile.java | 6 +- .../comm/MsgStatusTempBasal.java | 6 +- .../comm/RecordTypes.java | 2 +- .../events/EventDanaRBolusStart.java | 2 +- .../events/EventDanaRNewStatus.java | 2 +- .../events/EventDanaRSyncStatus.java | 2 +- .../DanaRKoreanFragment.java | 10 +- .../DanaRKoreanPlugin.java | 6 +- .../DanaRKoreanPump.java | 2 +- .../History/DanaRHistoryActivity.java | 10 +- .../History/DanaRStatsActivity.java | 10 +- .../SerialIOThread.java | 6 +- .../Services/ExecutionService.java | 82 ++++++++-------- .../comm/MessageHashTable.java | 44 ++++----- .../comm/MsgCheckValue.java | 8 +- .../comm/MsgInitConnStatusBasic.java | 8 +- .../comm/MsgInitConnStatusBolus.java | 8 +- .../comm/MsgInitConnStatusTime.java | 8 +- .../comm/MsgSettingBasal.java | 8 +- .../comm/MsgSettingBasalProfileAll.java | 8 +- .../comm/MsgSettingGlucose.java | 8 +- .../comm/MsgSettingMaxValues.java | 6 +- .../comm/MsgSettingMeal.java | 10 +- .../comm/MsgSettingProfileRatios.java | 8 +- .../comm/MsgSettingPumpTime.java | 6 +- .../comm/MsgSettingShippingInfo.java | 10 +- .../comm/MsgStatus.java | 6 +- .../comm/MsgStatusBasic.java | 6 +- .../comm/MsgStatusBolusExtended.java | 8 +- .../comm/MsgStatusProfile.java | 8 +- .../comm/MsgStatusTempBasal.java | 8 +- .../plugins/PumpMDI/MDIFragment.java | 19 ++++ .../plugins/{MDI => PumpMDI}/MDIPlugin.java | 2 +- .../VirtualPumpFragment.java | 4 +- .../VirtualPumpPlugin.java | 4 +- .../events/EventVirtualPumpUpdateGui.java | 2 +- .../SmsCommunicatorPlugin.java | 6 +- .../circadianpercentageprofile_fragment.xml | 2 +- app/src/main/res/layout/danar_fragment.xml | 2 +- .../main/res/layout/danar_historyactivity.xml | 2 +- .../main/res/layout/danar_statsactivity.xml | 2 +- .../main/res/layout/localprofile_fragment.xml | 2 +- .../res/layout/nsprofileviewer_fragment.xml | 2 +- .../main/res/layout/objectives_fragment.xml | 2 +- .../res/layout/simpleprofile_fragment.xml | 2 +- .../main/res/layout/vitualpump_fragment.xml | 2 +- app/src/main/res/xml/pref_danar.xml | 2 +- 134 files changed, 400 insertions(+), 462 deletions(-) rename app/src/main/java/info/nightscout/androidaps/plugins/{Objectives => ConstraintsObjectives}/ObjectivesFragment.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{Objectives => ConstraintsObjectives}/ObjectivesPlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{SafetyFragment => ConstraintsSafety}/SafetyFragment.java (88%) rename app/src/main/java/info/nightscout/androidaps/plugins/{SafetyFragment => ConstraintsSafety}/SafetyPlugin.java (99%) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/MDI/MDIFragment.java rename app/src/main/java/info/nightscout/androidaps/plugins/{CircadianPercentageProfile => ProfileCircadianPercentage}/CircadianPercentageProfileFragment.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{CircadianPercentageProfile => ProfileCircadianPercentage}/CircadianPercentageProfilePlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{LocalProfile => ProfileLocal}/LocalProfileFragment.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{LocalProfile => ProfileLocal}/LocalProfilePlugin.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{NSProfile => ProfileNS}/NSProfileFragment.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{NSProfile => ProfileNS}/NSProfilePlugin.java (97%) rename app/src/main/java/info/nightscout/androidaps/plugins/{NSProfile => ProfileNS}/events/EventNSProfileUpdateGUI.java (57%) rename app/src/main/java/info/nightscout/androidaps/plugins/{SimpleProfile => ProfileSimple}/SimpleProfileFragment.java (95%) rename app/src/main/java/info/nightscout/androidaps/plugins/{SimpleProfile => ProfileSimple}/SimpleProfilePlugin.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/BluetoothDevicePreference.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/DanaRFragment.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/DanaRPlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/DanaRPump.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/Dialogs/ProfileViewDialog.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/History/DanaRHistoryActivity.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/History/DanaRNSHistorySync.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/History/DanaRStatsActivity.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/SerialIOThread.java (97%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/Services/ExecutionService.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MessageBase.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MessageHashTable.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MessageOriginalNames.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgBolusProgress.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgBolusStart.java (91%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgBolusStop.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgCheckValue.java (88%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgError.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryAlarm.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryAll.java (97%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryAllDone.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryBasalHour.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryBolus.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryCarbo.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryDailyInsulin.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryDone.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryError.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryGlucose.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryNew.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryNewDone.java (91%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistoryRefill.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgHistorySuspend.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgInitConnStatusBasic.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgInitConnStatusBolus.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgInitConnStatusOption.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgInitConnStatusTime.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgPCCommStart.java (88%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgPCCommStop.java (88%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetActivateBasalProfile.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetBasalProfile.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetCarbsEntry.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetExtendedBolusStart.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetExtendedBolusStop.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetSingleBasalProfile.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetTempBasalStart.java (95%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetTempBasalStop.java (92%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSetTime.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingActiveProfile.java (83%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingBasal.java (85%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingBasalProfileAll.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingGlucose.java (81%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingMaxValues.java (88%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingMeal.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingProfileRatios.java (91%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingProfileRatiosAll.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingPumpTime.java (87%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingShippingInfo.java (81%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgSettingUserOptions.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgStatus.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgStatusBasic.java (91%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgStatusBolusExtended.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgStatusProfile.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/MsgStatusTempBasal.java (95%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/comm/RecordTypes.java (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/events/EventDanaRBolusStart.java (57%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/events/EventDanaRNewStatus.java (56%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaR => PumpDanaR}/events/EventDanaRSyncStatus.java (79%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/DanaRKoreanFragment.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/DanaRKoreanPlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/DanaRKoreanPump.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/History/DanaRHistoryActivity.java (97%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/History/DanaRStatsActivity.java (98%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/SerialIOThread.java (97%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/Services/ExecutionService.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MessageHashTable.java (71%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgCheckValue.java (84%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgInitConnStatusBasic.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgInitConnStatusBolus.java (87%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgInitConnStatusTime.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingBasal.java (80%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingBasalProfileAll.java (90%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingGlucose.java (74%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingMaxValues.java (82%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingMeal.java (82%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingProfileRatios.java (87%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingPumpTime.java (81%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgSettingShippingInfo.java (75%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgStatus.java (89%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgStatusBasic.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgStatusBolusExtended.java (94%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgStatusProfile.java (86%) rename app/src/main/java/info/nightscout/androidaps/plugins/{DanaRKorean => PumpDanaRKorean}/comm/MsgStatusTempBasal.java (94%) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpMDI/MDIFragment.java rename app/src/main/java/info/nightscout/androidaps/plugins/{MDI => PumpMDI}/MDIPlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{VirtualPump => PumpVirtual}/VirtualPumpFragment.java (96%) rename app/src/main/java/info/nightscout/androidaps/plugins/{VirtualPump => PumpVirtual}/VirtualPumpPlugin.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/{VirtualPump => PumpVirtual}/events/EventVirtualPumpUpdateGui.java (57%) diff --git a/.travis.yml b/.travis.yml index 8429db4b11..f76f8f5fd9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,13 @@ language: android jdk: oraclejdk8 env: matrix: - - ANDROID_TARGET=android-23 ANDROID_ABI=x86 + - ANDROID_TARGET=android-25 ANDROID_ABI=x86 android: components: - platform-tools - tools - build-tools-25.0.2 - - android-23 + - android-25 - extra-google-m2repository - extra-android-m2repository - extra-google-google_play_services diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e01ed53f34..ab45d3d4f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,10 +41,10 @@ android:name=".plugins.Overview.Dialogs.BolusProgressHelperActivity" android:theme="@style/Theme.AppCompat.Translucent" /> - - - - + + + + @@ -116,11 +116,11 @@ android:name=".Services.DataService" android:exported="false" /> + tools:context=".plugins.ProfileCircadianPercentage.CircadianPercentageProfileFragment"> + tools:context="info.nightscout.androidaps.plugins.PumpDanaR.DanaRFragment"> + tools:context=".plugins.PumpDanaR.History.DanaRHistoryActivity"> + tools:context=".plugins.PumpDanaR.History.DanaRHistoryActivity"> + tools:context=".plugins.ProfileLocal.LocalProfileFragment"> + tools:context=".plugins.ProfileNS.NSProfileFragment"> + tools:context=".plugins.ConstraintsObjectives.ObjectivesFragment"> + tools:context=".plugins.ProfileSimple.SimpleProfileFragment"> + tools:context=".plugins.PumpVirtual.VirtualPumpFragment"> - From 0a2105ac50ab55a17d8980e8dfd3d5267ed04c74 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 12:27:16 +0200 Subject: [PATCH 005/213] wear target 25 --- wear/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wear/build.gradle b/wear/build.gradle index 1bffedf90b..db3acfa5dc 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -2,13 +2,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 + compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "info.nightscout.androidaps" minSdkVersion 20 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1 versionName "1.0.2" } @@ -65,6 +65,6 @@ dependencies { compile 'com.google.android.gms:play-services-wearable:7.3.0' compile files('libs/hellocharts-library-1.5.5.jar') compile(name:'ustwo-clockwise-debug', ext:'aar') - compile 'com.android.support:support-v4:23.0.1' + compile 'com.android.support:support-v4:25.3.1' compile 'me.denley.wearpreferenceactivity:wearpreferenceactivity:0.5.0' } From 90a55966b1907467eea5edeba4d601fbed030c4b Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 15:36:49 +0200 Subject: [PATCH 006/213] Fast Acting Insulin Prolonged --- app/build.gradle | 6 + .../info/nightscout/androidaps/MainApp.java | 2 + .../interfaces/InsulinInterface.java | 1 + .../InsulinFastactingFragment.java | 38 ++++++ .../InsulinFastactingProlongedFragment.java | 57 ++++++++ .../InsulinFastactingProlongedPlugin.java | 129 ++++++++++++++++++ app/src/main/res/drawable/insulin1.png | Bin 0 -> 36866 bytes app/src/main/res/layout/insulin_fragment.xml | 41 ++++++ app/src/main/res/values/strings.xml | 1 + 9 files changed, 275 insertions(+) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java create mode 100644 app/src/main/res/drawable/insulin1.png create mode 100644 app/src/main/res/layout/insulin_fragment.xml diff --git a/app/build.gradle b/app/build.gradle index 0d9ffed984..9bda1ceddc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,9 +48,15 @@ android { buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", generateGitBuild() } + + dexOptions { + jumboMode true + } + lintOptions { disable 'MissingTranslation' } + buildTypes { release { minifyEnabled false diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 1678b42405..358c4e4d38 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -22,6 +22,7 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.Actions.ActionsFragment; import info.nightscout.androidaps.plugins.Careportal.CareportalFragment; +import info.nightscout.androidaps.plugins.InsulinFastactingProlonged.InsulinFastactingProlongedFragment; import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfileFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; @@ -88,6 +89,7 @@ public class MainApp extends Application { pluginsList.add(OverviewFragment.getPlugin()); if (Config.ACTION) pluginsList.add(ActionsFragment.getPlugin()); pluginsList.add(InsulinFastactingFragment.getPlugin()); + pluginsList.add(InsulinFastactingProlongedFragment.getPlugin()); if (Config.DANAR) pluginsList.add(DanaRFragment.getPlugin()); if (Config.DANARKOREAN) pluginsList.add(DanaRKoreanFragment.getPlugin()); pluginsList.add(CareportalFragment.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java index 05261e2916..81359b4329 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java @@ -11,6 +11,7 @@ import info.nightscout.androidaps.db.Treatment; public interface InsulinInterface { final int FASTACTINGINSULIN = 0; + final int FASTACTINGINSULINPROLONGED = 1; int getId(); String getFriendlyName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java index bff8436861..df29240c55 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java @@ -1,7 +1,18 @@ package info.nightscout.androidaps.plugins.InsulinFastacting; +import android.os.Bundle; import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.FragmentBase; /** @@ -15,5 +26,32 @@ public class InsulinFastactingFragment extends Fragment implements FragmentBase return insulinFastactingPlugin; } + private Unbinder unbinder; + @BindView(R.id.insulin_name) + TextView insulinName; + @BindView(R.id.insulin_comment) + TextView insulinComment; + @BindView(R.id.insulin_activity) + ImageView insulinActivity; + + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.insulin_fragment, container, false); + + unbinder = ButterKnife.bind(this, view); + + insulinName.setText(insulinFastactingPlugin.getFriendlyName()); + insulinComment.setText(insulinFastactingPlugin.getComment()); + insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingPlugin.getResourcePicture())); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java new file mode 100644 index 0000000000..648c47a41b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java @@ -0,0 +1,57 @@ +package info.nightscout.androidaps.plugins.InsulinFastactingProlonged; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.FragmentBase; + +/** + * Created by mike on 17.04.2017. + */ + +public class InsulinFastactingProlongedFragment extends Fragment implements FragmentBase { + static InsulinFastactingProlongedPlugin insulinFastactingProlongedPlugin = new InsulinFastactingProlongedPlugin(); + + static public InsulinFastactingProlongedPlugin getPlugin() { + return insulinFastactingProlongedPlugin; + } + + private Unbinder unbinder; + @BindView(R.id.insulin_name) + TextView insulinName; + @BindView(R.id.insulin_comment) + TextView insulinComment; + @BindView(R.id.insulin_activity) + ImageView insulinActivity; + + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.insulin_fragment, container, false); + + unbinder = ButterKnife.bind(this, view); + + insulinName.setText(insulinFastactingProlongedPlugin.getFriendlyName()); + insulinComment.setText(insulinFastactingProlongedPlugin.getComment()); + insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingProlongedPlugin.getResourcePicture())); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java new file mode 100644 index 0000000000..0eeb01f199 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java @@ -0,0 +1,129 @@ +package info.nightscout.androidaps.plugins.InsulinFastactingProlonged; + +import java.util.Date; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.interfaces.InsulinInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.ProfileInterface; + +/** + * Created by mike on 17.04.2017. + */ + +public class InsulinFastactingProlongedPlugin implements PluginBase, InsulinInterface { + + private static boolean fragmentEnabled = true; + private static boolean fragmentVisible = false; + + @Override + public int getType() { + return INSULIN; + } + + @Override + public String getFragmentClass() { + return InsulinFastactingProlongedFragment.class.getName(); + } + + @Override + public String getName() { + return MainApp.sResources.getString(R.string.fastactinginsulinprolonged); + } + + @Override + public String getNameShort() { + return MainApp.sResources.getString(R.string.insulin_shortname); + } + + @Override + public boolean isEnabled(int type) { + return type == INSULIN && fragmentEnabled; + } + + @Override + public boolean isVisibleInTabs(int type) { + return type == INSULIN && fragmentVisible; + } + + @Override + public boolean canBeHidden(int type) { + return true; + } + + @Override + public void setFragmentEnabled(int type, boolean fragmentEnabled) { + if (type == INSULIN) this.fragmentEnabled = fragmentEnabled; + } + + @Override + public void setFragmentVisible(int type, boolean fragmentVisible) { + if (type == INSULIN) this.fragmentVisible = fragmentVisible; + } + + // Insulin interface + @Override + public int getId() { + return FASTACTINGINSULINPROLONGED; + } + + @Override + public String getFriendlyName() { + return MainApp.sResources.getString(R.string.fastactinginsulinprolonged); + } + + @Override + public String getComment() { + return MainApp.sResources.getString(R.string.fastactinginsulincomment); + } + + @Override + public int getResourcePicture() { + return R.drawable.insulin1; + } + + @Override + public double getDia() { + ProfileInterface profileInterface = MainApp.getConfigBuilder().getActiveProfile(); + if (profileInterface.getProfile() != null) + return profileInterface.getProfile().getDia(); + return Constants.defaultDIA; + } + + @Override + public Iob iobCalc(Treatment treatment, Date time, Double dia) { + Iob result = new Iob(); + + //Double scaleFactor = 3.0 / dia; + Double peak = 75d * dia / 6.0; + Double tail = 180d * dia / 6.0; + Double end = 360d * dia / 6.0; + Double Total = 2 * peak + (tail - peak) * 5 / 2 + (end - tail) / 2; + + if (treatment.insulin != 0d) { + Long bolusTime = treatment.created_at.getTime(); + Double minAgo = (time.getTime() - bolusTime) / 1000d / 60d; + + if (minAgo < peak) { + Double x1 = 6 / dia * minAgo / 5d + 1; + result.iobContrib = treatment.insulin * (1 - 0.0012595 * x1 * x1 + 0.0012595 * x1); + // units: BG (mg/dL) = (BG/U) * U insulin * scalar + result.activityContrib = treatment.insulin * ((2 * peak / Total) * 2 / peak / peak * minAgo); + } else if (minAgo < tail) { + Double x2 = (6 / dia * (minAgo - peak)) / 5; + result.iobContrib = treatment.insulin * (0.00074 * x2 * x2 - 0.0403 * x2 + 0.69772); + result.activityContrib = treatment.insulin * (-((2 * peak / Total) * 2 / peak * 3 / 4) / (tail - peak) * (minAgo - peak) + (2 * peak / Total) * 2 / peak); + } else if (minAgo < end) { + Double x3 = (6 / dia * (minAgo - tail)) / 5; + result.iobContrib = treatment.insulin * (0.0001323 * x3 * x3 - 0.0097 * x3 + 0.17776); + result.activityContrib = treatment.insulin * (-((2 * peak / Total) * 2 / peak * 1 / 4) / (end - tail) * (minAgo - tail) + (2 * peak / Total) * 2 / peak / 4); + } + + } + return result; + } +} diff --git a/app/src/main/res/drawable/insulin1.png b/app/src/main/res/drawable/insulin1.png new file mode 100644 index 0000000000000000000000000000000000000000..36cc3af463280c3dacff2dde0ad27920dde1d0de GIT binary patch literal 36866 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

Gv9<4zeDn;L}j)ND@KLpeeW^YMxx5 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/insulin_fragment.xml b/app/src/main/res/layout/insulin_fragment.xml new file mode 100644 index 0000000000..86fccb49c3 --- /dev/null +++ b/app/src/main/res/layout/insulin_fragment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30046ec0c0..cee9303d4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -588,4 +588,5 @@ Fast Acting Insulin Novorapid, Novolog, Humalog INS + Fast Acting Insuin Prolonged From 7f122f7497504b0945d7c977e3ac23904007bf12 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 16:13:53 +0200 Subject: [PATCH 007/213] show dia in insulin plugin --- .../InsulinFastacting/InsulinFastactingFragment.java | 3 +++ .../InsulinFastactingProlongedFragment.java | 3 +++ app/src/main/res/layout/insulin_fragment.xml | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java index df29240c55..2f62a9a9c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java @@ -31,6 +31,8 @@ public class InsulinFastactingFragment extends Fragment implements FragmentBase TextView insulinName; @BindView(R.id.insulin_comment) TextView insulinComment; + @BindView(R.id.insulin_dia) + TextView insulinDia; @BindView(R.id.insulin_activity) ImageView insulinActivity; @@ -42,6 +44,7 @@ public class InsulinFastactingFragment extends Fragment implements FragmentBase insulinName.setText(insulinFastactingPlugin.getFriendlyName()); insulinComment.setText(insulinFastactingPlugin.getComment()); + insulinDia.setText(MainApp.sResources.getText(R.string.dia) + " " + new Double(insulinFastactingPlugin.getDia()).toString() + "h"); insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingPlugin.getResourcePicture())); return view; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java index 648c47a41b..305223bea7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java @@ -31,6 +31,8 @@ public class InsulinFastactingProlongedFragment extends Fragment implements Frag TextView insulinName; @BindView(R.id.insulin_comment) TextView insulinComment; + @BindView(R.id.insulin_dia) + TextView insulinDia; @BindView(R.id.insulin_activity) ImageView insulinActivity; @@ -42,6 +44,7 @@ public class InsulinFastactingProlongedFragment extends Fragment implements Frag insulinName.setText(insulinFastactingProlongedPlugin.getFriendlyName()); insulinComment.setText(insulinFastactingProlongedPlugin.getComment()); + insulinDia.setText(MainApp.sResources.getText(R.string.dia) + " " + new Double(insulinFastactingProlongedPlugin.getDia()).toString() + "h"); insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingProlongedPlugin.getResourcePicture())); return view; diff --git a/app/src/main/res/layout/insulin_fragment.xml b/app/src/main/res/layout/insulin_fragment.xml index 86fccb49c3..0aa2b001b9 100644 --- a/app/src/main/res/layout/insulin_fragment.xml +++ b/app/src/main/res/layout/insulin_fragment.xml @@ -28,6 +28,13 @@ android:layout_margin="10dp" android:textAppearance="?android:attr/textAppearanceMedium" /> + + Date: Fri, 21 Apr 2017 18:34:40 +0200 Subject: [PATCH 008/213] insulin activity graph --- .../interfaces/InsulinInterface.java | 1 - .../InsulinFastacting/ActivityGraph.java | 78 ++++++++++++++++++ .../InsulinFastactingFragment.java | 6 +- .../InsulinFastactingPlugin.java | 5 -- .../InsulinFastactingProlongedFragment.java | 7 +- .../InsulinFastactingProlongedPlugin.java | 5 -- app/src/main/res/drawable/insulin0.png | Bin 29335 -> 0 bytes app/src/main/res/drawable/insulin1.png | Bin 36866 -> 0 bytes app/src/main/res/layout/insulin_fragment.xml | 8 +- 9 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java delete mode 100644 app/src/main/res/drawable/insulin0.png delete mode 100644 app/src/main/res/drawable/insulin1.png diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java index 81359b4329..022421105f 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java @@ -16,7 +16,6 @@ public interface InsulinInterface { int getId(); String getFriendlyName(); String getComment(); - int getResourcePicture(); double getDia(); public Iob iobCalc(Treatment treatment, Date time, Double dia); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java new file mode 100644 index 0000000000..7ba486a3f3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java @@ -0,0 +1,78 @@ +package info.nightscout.androidaps.plugins.InsulinFastacting; + +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; + +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.series.DataPoint; +import com.jjoe64.graphview.series.LineGraphSeries; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.interfaces.InsulinInterface; +import info.nightscout.androidaps.plugins.Overview.graphExtensions.TimeAsXAxisLabelFormatter; + +/** + * Created by mike on 21.04.2017. + */ + +public class ActivityGraph extends GraphView { + + Context context; + + public ActivityGraph(Context context) { + super(context); + this.context = context; + } + + public ActivityGraph(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + } + + public void show(InsulinInterface insulin) { + double dia = insulin.getDia(); + int hours = (int) Math.floor(dia + 1); + + Treatment t = new Treatment(insulin); + t.created_at = new Date(0); + t.timeIndex = 0; + t.insulin = 1d; + + LineGraphSeries activitySeries = null; + LineGraphSeries iobSeries = null; + List activityArray = new ArrayList(); + List iobArray = new ArrayList(); + + for (long time = 0; time <= hours * 60 * 60 * 1000; time += 5 * 60 * 1000L) { + Iob iob = insulin.iobCalc(t, new Date(time), dia); + activityArray.add(new DataPoint(time / 60 / 1000, iob.activityContrib)); + iobArray.add(new DataPoint(time / 60 / 1000, iob.iobContrib)); + } + + DataPoint[] activityDataPoints = new DataPoint[activityArray.size()]; + activityDataPoints = activityArray.toArray(activityDataPoints); + addSeries(activitySeries = new LineGraphSeries(activityDataPoints)); + activitySeries.setThickness(8); + + getViewport().setXAxisBoundsManual(true); + getViewport().setMinX(0); + getViewport().setMaxX(hours * 60); + getGridLabelRenderer().setNumHorizontalLabels(hours + 1); + getGridLabelRenderer().setHorizontalAxisTitle("[min]"); + + DataPoint[] iobDataPoints = new DataPoint[iobArray.size()]; + iobDataPoints = iobArray.toArray(iobDataPoints); + getSecondScale().addSeries(iobSeries = new LineGraphSeries(iobDataPoints)); + iobSeries.setDrawBackground(true); + iobSeries.setColor(Color.MAGENTA); + iobSeries.setBackgroundColor(Color.argb(70, 255, 0, 255)); + getSecondScale().setMinY(0); + getSecondScale().setMaxY(1); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java index 2f62a9a9c3..d4444e2fe8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingFragment.java @@ -33,8 +33,8 @@ public class InsulinFastactingFragment extends Fragment implements FragmentBase TextView insulinComment; @BindView(R.id.insulin_dia) TextView insulinDia; - @BindView(R.id.insulin_activity) - ImageView insulinActivity; + @BindView(R.id.insuling_graph) + ActivityGraph insulinGraph; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -45,7 +45,7 @@ public class InsulinFastactingFragment extends Fragment implements FragmentBase insulinName.setText(insulinFastactingPlugin.getFriendlyName()); insulinComment.setText(insulinFastactingPlugin.getComment()); insulinDia.setText(MainApp.sResources.getText(R.string.dia) + " " + new Double(insulinFastactingPlugin.getDia()).toString() + "h"); - insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingPlugin.getResourcePicture())); + insulinGraph.show(insulinFastactingPlugin); return view; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java index a43bccf927..baf28ce1b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/InsulinFastactingPlugin.java @@ -81,11 +81,6 @@ public class InsulinFastactingPlugin implements PluginBase, InsulinInterface { return MainApp.sResources.getString(R.string.fastactinginsulincomment); } - @Override - public int getResourcePicture() { - return R.drawable.insulin0; - } - @Override public double getDia() { ProfileInterface profileInterface = MainApp.getConfigBuilder().getActiveProfile(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java index 305223bea7..d3be3128dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedFragment.java @@ -14,6 +14,7 @@ import butterknife.Unbinder; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.FragmentBase; +import info.nightscout.androidaps.plugins.InsulinFastacting.ActivityGraph; /** * Created by mike on 17.04.2017. @@ -33,8 +34,8 @@ public class InsulinFastactingProlongedFragment extends Fragment implements Frag TextView insulinComment; @BindView(R.id.insulin_dia) TextView insulinDia; - @BindView(R.id.insulin_activity) - ImageView insulinActivity; + @BindView(R.id.insuling_graph) + ActivityGraph insulinGraph; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -45,7 +46,7 @@ public class InsulinFastactingProlongedFragment extends Fragment implements Frag insulinName.setText(insulinFastactingProlongedPlugin.getFriendlyName()); insulinComment.setText(insulinFastactingProlongedPlugin.getComment()); insulinDia.setText(MainApp.sResources.getText(R.string.dia) + " " + new Double(insulinFastactingProlongedPlugin.getDia()).toString() + "h"); - insulinActivity.setImageDrawable(MainApp.sResources.getDrawable(insulinFastactingProlongedPlugin.getResourcePicture())); + insulinGraph.show(insulinFastactingProlongedPlugin); return view; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java index 0eeb01f199..fe11566252 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastactingProlonged/InsulinFastactingProlongedPlugin.java @@ -81,11 +81,6 @@ public class InsulinFastactingProlongedPlugin implements PluginBase, InsulinInte return MainApp.sResources.getString(R.string.fastactinginsulincomment); } - @Override - public int getResourcePicture() { - return R.drawable.insulin1; - } - @Override public double getDia() { ProfileInterface profileInterface = MainApp.getConfigBuilder().getActiveProfile(); diff --git a/app/src/main/res/drawable/insulin0.png b/app/src/main/res/drawable/insulin0.png deleted file mode 100644 index f0cbe937f8e96696f36f9398c00ef07c7eb5a73a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29335 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

Gv9<4zeDn;L}j)ND@KLpeeW^YMxx5 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 diff --git a/app/src/main/res/layout/insulin_fragment.xml b/app/src/main/res/layout/insulin_fragment.xml index 0aa2b001b9..e9a172ae87 100644 --- a/app/src/main/res/layout/insulin_fragment.xml +++ b/app/src/main/res/layout/insulin_fragment.xml @@ -35,12 +35,12 @@ android:layout_margin="10dp" android:textAppearance="?android:attr/textAppearanceMedium" /> - + android:layout_height="200dip" /> + From 2077b5e081bc14290d33727b39735876719b72e2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 18:56:48 +0200 Subject: [PATCH 009/213] android support library bug workaround --- .../nightscout/androidaps/tabs/TabPageAdapter.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java index cedae6e9d7..4f7b4a7a30 100644 --- a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java @@ -7,6 +7,7 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; +import android.view.ViewGroup; import java.util.ArrayList; @@ -36,6 +37,15 @@ public class TabPageAdapter extends FragmentStatePagerAdapter { return Fragment.instantiate(context, visibleFragmentList.get(position).getFragmentClass()); } + @Override + public void finishUpdate(ViewGroup container) { + try{ + super.finishUpdate(container); + } catch (NullPointerException nullPointerException){ + System.out.println("Catch the NullPointerException in FragmentStatePagerAdapter.finishUpdate"); + } + } + @Override public CharSequence getPageTitle(int position) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); @@ -58,4 +68,6 @@ public class TabPageAdapter extends FragmentStatePagerAdapter { notifyDataSetChanged(); } } + + } From a8146da8a83cb727177b9fb39b9fcf7b22c0b140 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 21 Apr 2017 23:06:54 +0200 Subject: [PATCH 010/213] target range with background color --- .../plugins/Overview/OverviewFragment.java | 23 +- .../graphExtensions/AreaGraphSeries.java | 456 ++++++++++++++++++ .../graphExtensions/DoubleDataPoint.java | 41 ++ 3 files changed, 507 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/AreaGraphSeries.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DoubleDataPoint.java 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 79b15a761d..c762f82dd0 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 @@ -86,6 +86,8 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.Overview.graphExtensions.AreaGraphSeries; +import info.nightscout.androidaps.plugins.Overview.graphExtensions.DoubleDataPoint; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.plugins.Overview.graphExtensions.TimeAsXAxisLabelFormatter; import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripPlugin; @@ -930,8 +932,7 @@ public class OverviewFragment extends Fragment { LineGraphSeries basalsLineSeries = null; BarGraphSeries basalsSeries = null; - LineGraphSeries seriesLow = null; - LineGraphSeries seriesHigh = null; + AreaGraphSeries areaSeries = null; LineGraphSeries seriesNow = null; PointsGraphSeries seriesInRage = null; PointsGraphSeries seriesOutOfRange = null; @@ -942,18 +943,14 @@ public class OverviewFragment extends Fragment { bgGraph.removeAllSeries(); // **** HIGH and LOW targets graph **** - DataPoint[] lowDataPoints = new DataPoint[]{ - new DataPoint(fromTime, lowLine), - new DataPoint(endTime, lowLine) + DoubleDataPoint[] areaDataPoints = new DoubleDataPoint[]{ + new DoubleDataPoint(fromTime, lowLine, highLine), + new DoubleDataPoint(endTime, lowLine, highLine) }; - DataPoint[] highDataPoints = new DataPoint[]{ - new DataPoint(fromTime, highLine), - new DataPoint(endTime, highLine) - }; - bgGraph.addSeries(seriesLow = new LineGraphSeries(lowDataPoints)); - seriesLow.setColor(Color.RED); - bgGraph.addSeries(seriesHigh = new LineGraphSeries(highDataPoints)); - seriesHigh.setColor(Color.RED); + bgGraph.addSeries(areaSeries = new AreaGraphSeries<>(areaDataPoints)); + areaSeries.setColor(0); + areaSeries.setDrawBackground(true); + areaSeries.setBackgroundColor(Color.argb(40, 0, 255, 0)); // **** TEMP BASALS graph **** class BarDataPoint extends DataPoint { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/AreaGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/AreaGraphSeries.java new file mode 100644 index 0000000000..08da836210 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/AreaGraphSeries.java @@ -0,0 +1,456 @@ +/** + * GraphView + * Copyright (C) 2014 Jonas Gehring + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * with the "Linking Exception", which can be found at the license.txt + * file in this program. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * with the "Linking Exception" along with this program; if not, + * write to the author Jonas Gehring . + */ +package info.nightscout.androidaps.plugins.Overview.graphExtensions; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; + +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.series.BaseSeries; +import com.jjoe64.graphview.series.DataPointInterface; + +import java.util.Iterator; + +/** + * Series to plot the data as line. + * The line can be styled with many options. + * + * @author jjoe64 + */ +public class AreaGraphSeries extends BaseSeries { + /** + * wrapped styles regarding the line + */ + private final class Styles { + /** + * the thickness of the line. + * This option will be ignored if you are + * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} + */ + private int thickness = 5; + + /** + * flag whether the area under the line to the bottom + * of the viewport will be filled with a + * specific background color. + * + * @see #backgroundColor + */ + private boolean drawBackground = false; + + /** + * flag whether the data points are highlighted as + * a visible point. + * + * @see #dataPointsRadius + */ + private boolean drawDataPoints = false; + + /** + * the radius for the data points. + * + * @see #drawDataPoints + */ + private float dataPointsRadius = 10f; + + /** + * the background color for the filling under + * the line. + * + * @see #drawBackground + */ + private int backgroundColor = Color.argb(100, 172, 218, 255); + } + + /** + * wrapped styles + */ + private Styles mStyles; + + /** + * internal paint object + */ + private Paint mPaint; + + /** + * paint for the background + */ + private Paint mPaintBackground; + + /** + * path for the background filling + */ + private Path mPathBackground; + + /** + * path to the line + */ + private Path mPath; + private Path mSecondPath; + + /** + * custom paint that can be used. + * this will ignore the thickness and color styles. + */ + private Paint mCustomPaint; + + /** + * creates a series without data + */ + public AreaGraphSeries() { + init(); + } + + /** + * creates a series with data + * + * @param data data points + */ + public AreaGraphSeries(E[] data) { + super(data); + init(); + } + + /** + * do the initialization + * creates internal objects + */ + protected void init() { + mStyles = new Styles(); + mPaint = new Paint(); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setStyle(Paint.Style.STROKE); + mPaintBackground = new Paint(); + + mPathBackground = new Path(); + mPath = new Path(); + mSecondPath = new Path(); + } + + /** + * plots the series + * draws the line and the background + * + * @param graphView graphview + * @param canvas canvas + * @param isSecondScale flag if it is the second scale + */ + @Override + public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { + resetDataPoints(); + + // get data + double maxX = graphView.getViewport().getMaxX(false); + double minX = graphView.getViewport().getMinX(false); + + double maxY; + double minY; + if (isSecondScale) { + maxY = graphView.getSecondScale().getMaxY(); + minY = graphView.getSecondScale().getMinY(); + } else { + maxY = graphView.getViewport().getMaxY(false); + minY = graphView.getViewport().getMinY(false); + } + + Iterator values = getValues(minX, maxX); + + // draw background + double lastEndY1 = 0; + double lastEndY2 = 0; + double lastEndX = 0; + + // draw data + mPaint.setStrokeWidth(mStyles.thickness); + mPaint.setColor(getColor()); + mPaintBackground.setColor(mStyles.backgroundColor); + + Paint paint; + if (mCustomPaint != null) { + paint = mCustomPaint; + } else { + paint = mPaint; + } + + if (mStyles.drawBackground) { + mPathBackground.reset(); + } + + double diffY = maxY - minY; + double diffX = maxX - minX; + + float graphHeight = graphView.getGraphContentHeight(); + float graphWidth = graphView.getGraphContentWidth(); + float graphLeft = graphView.getGraphContentLeft(); + float graphTop = graphView.getGraphContentTop(); + + lastEndY1 = 0; + lastEndY2 = 0; + lastEndX = 0; + double lastUsedEndX = 0; + float firstX = 0; + int i=0; + while (values.hasNext()) { + E value = values.next(); + + double valY1 = value.getY() - minY; + double ratY1 = valY1 / diffY; + double y1 = graphHeight * ratY1; + + double valY2 = value.getY2() - minY; + double ratY2 = valY2 / diffY; + double y2 = graphHeight * ratY2; + + double valX = value.getX() - minX; + double ratX = valX / diffX; + double x = graphWidth * ratX; + + double orgX = x; + double orgY1 = y1; + double orgY2 = y2; + + if (i > 0) { + // overdraw + if (x > graphWidth) { // end right + double b = ((graphWidth - lastEndX) * (y1 - lastEndY1)/(x - lastEndX)); + y1 = lastEndY1+b; + x = graphWidth; + } + if (x > graphWidth) { // end right + double b = ((graphWidth - lastEndX) * (y2 - lastEndY2)/(x - lastEndX)); + y2 = lastEndY2+b; + x = graphWidth; + } + if (y1 < 0) { // end bottom + double b = ((0 - lastEndY1) * (x - lastEndX)/(y1 - lastEndY1)); + x = lastEndX+b; + y1 = 0; + } + if (y2 < 0) { // end bottom + double b = ((0 - lastEndY2) * (x - lastEndX)/(y2 - lastEndY2)); + x = lastEndX+b; + y2 = 0; + } + if (y1 > graphHeight) { // end top + double b = ((graphHeight - lastEndY1) * (x - lastEndX)/(y1 - lastEndY1)); + x = lastEndX+b; + y1 = graphHeight; + } + if (y2 > graphHeight) { // end top + double b = ((graphHeight - lastEndY2) * (x - lastEndX)/(y2 - lastEndY2)); + x = lastEndX+b; + y2 = graphHeight; + } + if (lastEndY1 < 0) { // start bottom + double b = ((0 - y1) * (x - lastEndX)/(lastEndY1 - y1)); + lastEndX = x-b; + lastEndY1 = 0; + } + if (lastEndY2 < 0) { // start bottom + double b = ((0 - y2) * (x - lastEndX)/(lastEndY2 - y2)); + lastEndX = x-b; + lastEndY2 = 0; + } + if (lastEndX < 0) { // start left + double b = ((0 - x) * (y1 - lastEndY1)/(lastEndX - x)); + lastEndY1 = y1-b; + lastEndX = 0; + } + if (lastEndX < 0) { // start left + double b = ((0 - x) * (y2 - lastEndY2)/(lastEndX - x)); + lastEndY2 = y2-b; + lastEndX = 0; + } + if (lastEndY1 > graphHeight) { // start top + double b = ((graphHeight - y1) * (x - lastEndX)/(lastEndY1 - y1)); + lastEndX = x-b; + lastEndY1 = graphHeight; + } + if (lastEndY2 > graphHeight) { // start top + double b = ((graphHeight - y2) * (x - lastEndX)/(lastEndY2 - y2)); + lastEndX = x-b; + lastEndY2 = graphHeight; + } + + float startX = (float) lastEndX + (graphLeft + 1); + float startY1 = (float) (graphTop - lastEndY1) + graphHeight; + float startY2 = (float) (graphTop - lastEndY2) + graphHeight; + float endX = (float) x + (graphLeft + 1); + float endY1 = (float) (graphTop - y1) + graphHeight; + float endY2 = (float) (graphTop - y2) + graphHeight; + + // draw data point + if (mStyles.drawDataPoints) { + //fix: last value was not drawn. Draw here now the end values + canvas.drawCircle(endX, endY1, mStyles.dataPointsRadius, mPaint); + canvas.drawCircle(endX, endY2, mStyles.dataPointsRadius, mPaint); + } + registerDataPoint(endX, endY1, value); + registerDataPoint(endX, endY2, value); + + mPath.reset(); + mSecondPath.reset(); + mPath.moveTo(startX, startY1); + mSecondPath.moveTo(startX, startY2); + mPath.lineTo(endX, endY1); + mSecondPath.lineTo(endX, endY2); + canvas.drawPath(mPath, paint); + canvas.drawPath(mSecondPath, paint); + if (mStyles.drawBackground) { + canvas.drawRect((float)startX, (float)startY2, endX, endY1, mPaintBackground); + } + } else if (mStyles.drawDataPoints) { + //fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above) + //float first_X = (float) x + (graphLeft + 1); + //float first_Y = (float) (graphTop - y) + graphHeight; + //TODO canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint); + } + lastEndY1 = orgY1; + lastEndY2 = orgY2; + lastEndX = orgX; + i++; + } + +/* + if (mStyles.drawBackground) { + // end / close path + mPathBackground.lineTo((float) lastUsedEndX, graphHeight + graphTop); + mPathBackground.lineTo(firstX, graphHeight + graphTop); + mPathBackground.close(); + canvas.drawPath(mPathBackground, mPaintBackground); + } +*/ + + } + + /** + * the thickness of the line. + * This option will be ignored if you are + * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} + * + * @return the thickness of the line + */ + public int getThickness() { + return mStyles.thickness; + } + + /** + * the thickness of the line. + * This option will be ignored if you are + * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} + * + * @param thickness thickness of the line + */ + public void setThickness(int thickness) { + mStyles.thickness = thickness; + } + + /** + * flag whether the area under the line to the bottom + * of the viewport will be filled with a + * specific background color. + * + * @return whether the background will be drawn + * @see #getBackgroundColor() + */ + public boolean isDrawBackground() { + return mStyles.drawBackground; + } + + /** + * flag whether the area under the line to the bottom + * of the viewport will be filled with a + * specific background color. + * + * @param drawBackground whether the background will be drawn + * @see #setBackgroundColor(int) + */ + public void setDrawBackground(boolean drawBackground) { + mStyles.drawBackground = drawBackground; + } + + /** + * flag whether the data points are highlighted as + * a visible point. + * + * @return flag whether the data points are highlighted + * @see #setDataPointsRadius(float) + */ + public boolean isDrawDataPoints() { + return mStyles.drawDataPoints; + } + + /** + * flag whether the data points are highlighted as + * a visible point. + * + * @param drawDataPoints flag whether the data points are highlighted + * @see #setDataPointsRadius(float) + */ + public void setDrawDataPoints(boolean drawDataPoints) { + mStyles.drawDataPoints = drawDataPoints; + } + + /** + * @return the radius for the data points. + * @see #setDrawDataPoints(boolean) + */ + public float getDataPointsRadius() { + return mStyles.dataPointsRadius; + } + + /** + * @param dataPointsRadius the radius for the data points. + * @see #setDrawDataPoints(boolean) + */ + public void setDataPointsRadius(float dataPointsRadius) { + mStyles.dataPointsRadius = dataPointsRadius; + } + + /** + * @return the background color for the filling under + * the line. + * @see #setDrawBackground(boolean) + */ + public int getBackgroundColor() { + return mStyles.backgroundColor; + } + + /** + * @param backgroundColor the background color for the filling under + * the line. + * @see #setDrawBackground(boolean) + */ + public void setBackgroundColor(int backgroundColor) { + mStyles.backgroundColor = backgroundColor; + } + + /** + * custom paint that can be used. + * this will ignore the thickness and color styles. + * + * @param customPaint the custom paint to be used for rendering the line + */ + public void setCustomPaint(Paint customPaint) { + this.mCustomPaint = customPaint; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DoubleDataPoint.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DoubleDataPoint.java new file mode 100644 index 0000000000..5ac5224c65 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DoubleDataPoint.java @@ -0,0 +1,41 @@ +package info.nightscout.androidaps.plugins.Overview.graphExtensions; + +import com.jjoe64.graphview.series.DataPointInterface; + +import java.io.Serializable; +import java.util.Date; + +/** + * Created by mike on 21.04.2017. + */ + +public class DoubleDataPoint implements DataPointInterface, Serializable { + private static final long serialVersionUID=1428267322645L; + + private double x; + private double y1; + private double y2; + + public DoubleDataPoint(double x, double y1, double y2) { + this.x=x; + this.y1=y1; + this.y2=y2; + } + + public double getX() { + return x; + } + + @Override + public double getY() { + return y1; + } + + public double getY1() { + return y1; + } + + public double getY2() { + return y2; + } +} \ No newline at end of file From 20f99dffe2524f7bdb408eedebc2c14993953e41 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 22 Apr 2017 10:09:54 +0200 Subject: [PATCH 011/213] basals rendering redesign --- .../plugins/Overview/OverviewFragment.java | 469 +++++++++--------- app/src/main/res/layout/overview_fragment.xml | 23 +- app/src/main/res/values/colors.xml | 3 + 3 files changed, 266 insertions(+), 229 deletions(-) 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 c762f82dd0..28d512f47c 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 @@ -3,14 +3,12 @@ package info.nightscout.androidaps.plugins.Overview; import android.annotation.SuppressLint; import android.app.Activity; import android.content.DialogInterface; -import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; -import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; @@ -33,8 +31,6 @@ import android.widget.TextView; import com.crashlytics.android.answers.Answers; import com.crashlytics.android.answers.CustomEvent; import com.jjoe64.graphview.GraphView; -import com.jjoe64.graphview.ValueDependentColor; -import com.jjoe64.graphview.series.BarGraphSeries; import com.jjoe64.graphview.series.DataPoint; import com.jjoe64.graphview.series.LineGraphSeries; import com.jjoe64.graphview.series.PointsGraphSeries; @@ -52,6 +48,11 @@ import java.util.Date; import java.util.Iterator; import java.util.List; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnCheckedChanged; +import butterknife.OnClick; +import butterknife.Unbinder; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -76,9 +77,10 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.Careportal.Dialogs.NewNSTreatmentDialog; 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.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; -import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.androidaps.plugins.OpenAPSAMA.DetermineBasalResultAMA; import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.CalibrationDialog; @@ -93,7 +95,6 @@ import info.nightscout.androidaps.plugins.Overview.graphExtensions.TimeAsXAxisLa import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripPlugin; import info.nightscout.androidaps.plugins.TempTargetRange.TempTargetRangePlugin; import info.nightscout.androidaps.plugins.TempTargetRange.events.EventTempTargetRangeChange; -import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; @@ -111,36 +112,65 @@ public class OverviewFragment extends Fragment { return overviewPlugin; } + private Unbinder unbinder; + + @BindView(R.id.overview_bg) TextView bgView; + @BindView(R.id.overview_arrow) TextView arrowView; + @BindView(R.id.overview_timeago) TextView timeAgoView; + @BindView(R.id.overview_delta) TextView deltaView; + @BindView(R.id.overview_avgdelta) TextView avgdeltaView; + @BindView(R.id.overview_runningtemp) TextView runningTempView; + @BindView(R.id.overview_basebasal) TextView baseBasalView; + @BindView(R.id.overview_basallayout) LinearLayout basalLayout; + @BindView(R.id.overview_activeprofile) TextView activeProfileView; + @BindView(R.id.overview_iob) TextView iobView; + @BindView(R.id.overview_apsmode) TextView apsModeView; + @BindView(R.id.overview_temptarget) TextView tempTargetView; + @BindView(R.id.overview_pumpstatus) TextView pumpStatusView; + @BindView(R.id.overview_looplayout) LinearLayout loopStatusLayout; + @BindView(R.id.overview_pumpstatuslayout) LinearLayout pumpStatusLayout; + @BindView(R.id.overview_bggraph) GraphView bgGraph; + @BindView(R.id.overview_showprediction) CheckBox showPredictionView; - + @BindView(R.id.overview_showbasals) + CheckBox showBasalsView; + @BindView(R.id.overview_notifications) RecyclerView notificationsView; - LinearLayoutManager llm; - + @BindView(R.id.overview_canceltemplayout) LinearLayout cancelTempLayout; + @BindView(R.id.overview_accepttemplayout) LinearLayout acceptTempLayout; + @BindView(R.id.overview_canceltempbutton) Button cancelTempButton; + @BindView(R.id.overview_treatmentbutton) Button treatmentButton; + @BindView(R.id.overview_wizardbutton) Button wizardButton; + @BindView(R.id.overview_calibrationbutton) Button calibrationButton; + @BindView(R.id.overview_accepttempbutton) Button acceptTempButton; + @BindView(R.id.overview_quickwizardbutton) Button quickWizardButton; + LinearLayoutManager llm; + Handler sLoopHandler = new Handler(); Runnable sRefreshLoop = null; @@ -161,162 +191,26 @@ public class OverviewFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.overview_fragment, container, false); - bgView = (TextView) view.findViewById(R.id.overview_bg); - arrowView = (TextView) view.findViewById(R.id.overview_arrow); - timeAgoView = (TextView) view.findViewById(R.id.overview_timeago); - deltaView = (TextView) view.findViewById(R.id.overview_delta); - avgdeltaView = (TextView) view.findViewById(R.id.overview_avgdelta); - runningTempView = (TextView) view.findViewById(R.id.overview_runningtemp); - baseBasalView = (TextView) view.findViewById(R.id.overview_basebasal); - basalLayout = (LinearLayout) view.findViewById(R.id.overview_basallayout); - activeProfileView = (TextView) view.findViewById(R.id.overview_activeprofile); - pumpStatusView = (TextView) view.findViewById(R.id.overview_pumpstatus); - loopStatusLayout = (LinearLayout) view.findViewById(R.id.overview_looplayout); - pumpStatusLayout = (LinearLayout) view.findViewById(R.id.overview_pumpstatuslayout); - iobView = (TextView) view.findViewById(R.id.overview_iob); - apsModeView = (TextView) view.findViewById(R.id.overview_apsmode); - tempTargetView = (TextView) view.findViewById(R.id.overview_temptarget); - bgGraph = (GraphView) view.findViewById(R.id.overview_bggraph); - cancelTempButton = (Button) view.findViewById(R.id.overview_canceltemp); - treatmentButton = (Button) view.findViewById(R.id.overview_treatment); - wizardButton = (Button) view.findViewById(R.id.overview_wizard); - cancelTempButton = (Button) view.findViewById(R.id.overview_canceltemp); - cancelTempLayout = (LinearLayout) view.findViewById(R.id.overview_canceltemplayout); - acceptTempButton = (Button) view.findViewById(R.id.overview_accepttempbutton); - acceptTempLayout = (LinearLayout) view.findViewById(R.id.overview_accepttemplayout); - quickWizardButton = (Button) view.findViewById(R.id.overview_quickwizard); - calibrationButton = (Button) view.findViewById(R.id.overview_calibration); - showPredictionView = (CheckBox) view.findViewById(R.id.overview_showprediction); + unbinder = ButterKnife.bind(this, view); - notificationsView = (RecyclerView) view.findViewById(R.id.overview_notifications); notificationsView.setHasFixedSize(true); llm = new LinearLayoutManager(view.getContext()); notificationsView.setLayoutManager(llm); showPredictionView.setChecked(SP.getBoolean("showprediction", false)); + showBasalsView.setChecked(SP.getBoolean("showbasals", false)); - showPredictionView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean("showprediction", showPredictionView.isChecked()); - editor.apply(); - updateGUI(); - } - }); - - treatmentButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - FragmentManager manager = getFragmentManager(); - NewTreatmentDialog treatmentDialogFragment = new NewTreatmentDialog(); - treatmentDialogFragment.show(manager, "TreatmentDialog"); - } - }); - - wizardButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - FragmentManager manager = getFragmentManager(); - WizardDialog wizardDialog = new WizardDialog(); - wizardDialog.show(manager, "WizardDialog"); - } - }); - - quickWizardButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - processQuickWizard(); - } - }); - - cancelTempButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - final PumpInterface pump = MainApp.getConfigBuilder(); - if (pump.isTempBasalInProgress()) { - sHandler.post(new Runnable() { - @Override - public void run() { - pump.cancelTempBasal(); - MainApp.bus().post(new EventTempBasalChange()); - Answers.getInstance().logCustom(new CustomEvent("CancelTemp")); - } - }); - } - } - }); - - calibrationButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - FragmentManager manager = getFragmentManager(); - CalibrationDialog calibrationDialog = new CalibrationDialog(); - calibrationDialog.show(manager, "CalibrationDialog"); - } - }); - - acceptTempButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (ConfigBuilderPlugin.getActiveLoop() != null) { - ConfigBuilderPlugin.getActiveLoop().invoke("Accept temp button", false); - final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; - if (finalLastRun != null && finalLastRun.lastAPSRun != null && finalLastRun.constraintsProcessed.changeRequested) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(getContext().getString(R.string.confirmation)); - builder.setMessage(getContext().getString(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); - builder.setPositiveButton(getContext().getString(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - sHandler.post(new Runnable() { - @Override - public void run() { - hideTempRecommendation(); - PumpEnactResult applyResult = MainApp.getConfigBuilder().applyAPSRequest(finalLastRun.constraintsProcessed); - if (applyResult.enacted) { - finalLastRun.setByPump = applyResult; - finalLastRun.lastEnact = new Date(); - finalLastRun.lastOpenModeAccept = new Date(); - MainApp.getConfigBuilder().uploadDeviceStatus(); - ObjectivesPlugin objectivesPlugin = (ObjectivesPlugin) MainApp.getSpecificPlugin(ObjectivesPlugin.class); - if (objectivesPlugin != null) { - objectivesPlugin.manualEnacts++; - objectivesPlugin.saveProgress(); - } - } - updateGUIIfVisible(); - } - }); - Answers.getInstance().logCustom(new CustomEvent("AcceptTemp")); - } - }); - builder.setNegativeButton(getContext().getString(R.string.cancel), null); - builder.show(); - } - updateGUI(); - } - } - }); - - pumpStatusView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (MainApp.getConfigBuilder().isSuspended() || !MainApp.getConfigBuilder().isInitialized()) - sHandler.post(new Runnable() { - @Override - public void run() { - MainApp.getConfigBuilder().refreshDataFromPump("RefreshClicked"); - } - }); - } - }); - - updateGUI(); + updateGUI("onCreateView"); return view; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); @@ -444,7 +338,59 @@ public class OverviewFragment extends Fragment { return super.onContextItemSelected(item); } - void processQuickWizard() { + @OnCheckedChanged(R.id.overview_showprediction) + public void onPredictionCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SP.putBoolean("showprediction", showPredictionView.isChecked()); + updateGUI("onPredictionCheckedChanged"); + } + + @OnCheckedChanged(R.id.overview_showbasals) + public void onBasalsCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SP.putBoolean("showbasals", showPredictionView.isChecked()); + updateGUI("onBasalsCheckedChanged"); + } + + @OnClick(R.id.overview_accepttempbutton) + public void onClickAcceptTemp(View view) { + if (ConfigBuilderPlugin.getActiveLoop() != null) { + ConfigBuilderPlugin.getActiveLoop().invoke("Accept temp button", false); + final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; + if (finalLastRun != null && finalLastRun.lastAPSRun != null && finalLastRun.constraintsProcessed.changeRequested) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(getContext().getString(R.string.confirmation)); + builder.setMessage(getContext().getString(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); + builder.setPositiveButton(getContext().getString(R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + sHandler.post(new Runnable() { + @Override + public void run() { + hideTempRecommendation(); + PumpEnactResult applyResult = MainApp.getConfigBuilder().applyAPSRequest(finalLastRun.constraintsProcessed); + if (applyResult.enacted) { + finalLastRun.setByPump = applyResult; + finalLastRun.lastEnact = new Date(); + finalLastRun.lastOpenModeAccept = new Date(); + MainApp.getConfigBuilder().uploadDeviceStatus(); + ObjectivesPlugin objectivesPlugin = (ObjectivesPlugin) MainApp.getSpecificPlugin(ObjectivesPlugin.class); + if (objectivesPlugin != null) { + objectivesPlugin.manualEnacts++; + objectivesPlugin.saveProgress(); + } + } + updateGUIIfVisible("onClickAcceptTemp"); + } + }); + Answers.getInstance().logCustom(new CustomEvent("AcceptTemp")); + } + }); + builder.setNegativeButton(getContext().getString(R.string.cancel), null); + builder.show(); + } + } + } + + @OnClick(R.id.overview_quickwizardbutton) + void onClickQuickwizard(View view) { final BgReading actualBg = GlucoseStatus.actualBg(); if (MainApp.getConfigBuilder() == null || ConfigBuilderPlugin.getActiveProfile() == null) // app not initialized yet return; @@ -541,6 +487,52 @@ public class OverviewFragment extends Fragment { } + @OnClick(R.id.overview_wizardbutton) + public void onClickWizard(View view) { + FragmentManager manager = getFragmentManager(); + WizardDialog wizardDialog = new WizardDialog(); + wizardDialog.show(manager, "WizardDialog"); + } + + @OnClick(R.id.overview_calibrationbutton) + public void onClickCalibration(View view) { + FragmentManager manager = getFragmentManager(); + CalibrationDialog calibrationDialog = new CalibrationDialog(); + calibrationDialog.show(manager, "CalibrationDialog"); + } + + @OnClick(R.id.overview_treatmentbutton) + public void onClickTreatment(View view) { + FragmentManager manager = getFragmentManager(); + NewTreatmentDialog treatmentDialogFragment = new NewTreatmentDialog(); + treatmentDialogFragment.show(manager, "TreatmentDialog"); + } + + @OnClick(R.id.overview_canceltempbutton) + public void onClickCanceltemp(View view) { + final PumpInterface pump = MainApp.getConfigBuilder(); + if (pump.isTempBasalInProgress()) { + sHandler.post(new Runnable() { + @Override + public void run() { + pump.cancelTempBasal(); + Answers.getInstance().logCustom(new CustomEvent("CancelTemp")); + } + }); + } + } + + @OnClick(R.id.overview_pumpstatus) + public void onClickPumpstatus(View view) { + if (MainApp.getConfigBuilder().isSuspended() || !MainApp.getConfigBuilder().isInitialized()) + sHandler.post(new Runnable() { + @Override + public void run() { + MainApp.getConfigBuilder().refreshDataFromPump("RefreshClicked"); + } + }); + } + @Override public void onPause() { super.onPause(); @@ -556,58 +548,58 @@ public class OverviewFragment extends Fragment { sRefreshLoop = new Runnable() { @Override public void run() { - updateGUIIfVisible(); + updateGUIIfVisible("refreshLoop"); sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); } }; sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); registerForContextMenu(apsModeView); - updateGUIIfVisible(); + updateGUIIfVisible("onResume"); } @Subscribe public void onStatusEvent(final EventInitializationChanged ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventInitializationChanged"); } @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventPreferenceChange"); } @Subscribe public void onStatusEvent(final EventRefreshGui ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventRefreshGui"); } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventTreatmentChange"); } @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventTempBasalChange"); } @Subscribe public void onStatusEvent(final EventNewBG ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventTempBasalChange"); } @Subscribe public void onStatusEvent(final EventNewOpenLoopNotification ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventNewOpenLoopNotification"); } @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventNewBasalProfile"); } @Subscribe public void onStatusEvent(final EventTempTargetRangeChange ev) { - updateGUIIfVisible(); + updateGUIIfVisible("EventTempTargetRangeChange"); } @Subscribe @@ -643,13 +635,13 @@ public class OverviewFragment extends Fragment { }); } - private void updateGUIIfVisible() { + private void updateGUIIfVisible(final String from) { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(new Runnable() { @Override public void run() { - updateGUI(); + updateGUI(from); } }); } @@ -669,7 +661,8 @@ public class OverviewFragment extends Fragment { } @SuppressLint("SetTextI18n") - public void updateGUI() { + public void updateGUI(String from) { + log.debug("updateGUI entered from: " + from); updateNotifications(); BgReading actualBG = GlucoseStatus.actualBg(); BgReading lastBG = GlucoseStatus.lastBg(); @@ -931,7 +924,8 @@ public class OverviewFragment extends Fragment { } LineGraphSeries basalsLineSeries = null; - BarGraphSeries basalsSeries = null; + LineGraphSeries baseBasalsSeries = null; + LineGraphSeries tempBasalsSeries = null; AreaGraphSeries areaSeries = null; LineGraphSeries seriesNow = null; PointsGraphSeries seriesInRage = null; @@ -939,7 +933,86 @@ public class OverviewFragment extends Fragment { PointsGraphSeries predSeries = null; PointsWithLabelGraphSeries seriesTreatments = null; + // **** TEMP BASALS graph **** + Double maxBasalValueFound = 0d; + + long now = new Date().getTime(); + if (pump.getPumpDescription().isTempBasalCapable && showBasalsView.isChecked()) { + List baseBasalArray = new ArrayList<>(); + List tempBasalArray = new ArrayList<>(); + List basalLineArray = new ArrayList<>(); + double lastLineBasal = 0; + double lastBaseBasal = 0; + double lastTempBasal = 0; + for (long time = fromTime; time < now; time += 5 * 60 * 1000L) { + TempBasal tb = MainApp.getConfigBuilder().getTempBasal(new Date(time)); + double baseBasalValue = profile.getBasal(NSProfile.secondsFromMidnight(new Date(time))); + double baseLineValue = baseBasalValue; + double tempBasalValue = 0; + double basal = 0d; + if (tb != null) { + tempBasalValue = tb.tempBasalConvertedToAbsolute(new Date(time)); + if (tempBasalValue != lastTempBasal) { + tempBasalArray.add(new DataPoint(time, lastTempBasal)); + tempBasalArray.add(new DataPoint(time, basal = tempBasalValue)); + } + if (lastBaseBasal != 0d) { + baseBasalArray.add(new DataPoint(time, lastBaseBasal)); + baseBasalArray.add(new DataPoint(time, 0d)); + lastBaseBasal = 0d; + } + } else { + if (baseBasalValue != lastBaseBasal) { + baseBasalArray.add(new DataPoint(time, lastBaseBasal)); + baseBasalArray.add(new DataPoint(time, basal = baseBasalValue)); + lastBaseBasal = baseBasalValue; + } + if (lastTempBasal != 0) { + tempBasalArray.add(new DataPoint(time, lastTempBasal)); + tempBasalArray.add(new DataPoint(time, 0d)); + } + } + + if (baseLineValue != lastLineBasal) { + basalLineArray.add(new DataPoint(time, lastLineBasal)); + basalLineArray.add(new DataPoint(time, baseLineValue)); + } + + lastLineBasal = baseLineValue; + lastTempBasal = tempBasalValue; + maxBasalValueFound = Math.max(maxBasalValueFound, basal); + } + basalLineArray.add(new DataPoint(now, lastLineBasal)); + baseBasalArray.add(new DataPoint(now, lastBaseBasal)); + tempBasalArray.add(new DataPoint(now, lastTempBasal)); + + DataPoint[] baseBasal = new DataPoint[baseBasalArray.size()]; + baseBasal = baseBasalArray.toArray(baseBasal); + baseBasalsSeries = new LineGraphSeries<>(baseBasal); + baseBasalsSeries.setDrawBackground(true); + baseBasalsSeries.setBackgroundColor(Color.argb(200, 0x3F, 0x51, 0xB5)); + baseBasalsSeries.setThickness(0); + + DataPoint[] tempBasal = new DataPoint[tempBasalArray.size()]; + tempBasal = tempBasalArray.toArray(tempBasal); + tempBasalsSeries = new LineGraphSeries<>(tempBasal); + tempBasalsSeries.setDrawBackground(true); + tempBasalsSeries.setBackgroundColor(Color.argb(200, 0x03, 0xA9, 0xF4)); + tempBasalsSeries.setThickness(0); + + DataPoint[] basalLine = new DataPoint[basalLineArray.size()]; + basalLine = basalLineArray.toArray(basalLine); + basalsLineSeries = new LineGraphSeries<>(basalLine); + Paint paint = new Paint(); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(2); + paint.setPathEffect(new DashPathEffect(new float[]{2, 4}, 0)); + paint.setColor(Color.CYAN); + basalsLineSeries.setCustomPaint(paint); + } + // remove old data from graph + bgGraph.getSecondScale().getSeries().clear(); bgGraph.removeAllSeries(); // **** HIGH and LOW targets graph **** @@ -952,57 +1025,6 @@ public class OverviewFragment extends Fragment { areaSeries.setDrawBackground(true); areaSeries.setBackgroundColor(Color.argb(40, 0, 255, 0)); - // **** TEMP BASALS graph **** - class BarDataPoint extends DataPoint { - public BarDataPoint(double x, double y, boolean isTempBasal) { - super(x, y); - this.isTempBasal = isTempBasal; - } - - public boolean isTempBasal = false; - } - - Double maxBasalValueFound = 0d; - - long now = new Date().getTime(); - if (pump.getPumpDescription().isTempBasalCapable) { - List basalArray = new ArrayList(); - List basalLineArray = new ArrayList(); - double lastBaseBasal = 0; - for (long time = fromTime; time < now; time += 5 * 60 * 1000L) { - TempBasal tb = MainApp.getConfigBuilder().getTempBasal(new Date(time)); - double basebasal = profile.getBasal(NSProfile.secondsFromMidnight(new Date(time))); - Double basal = 0d; - if (tb != null) - basalArray.add(new BarDataPoint(time, basal = tb.tempBasalConvertedToAbsolute(new Date(time)), true)); - else { - basalArray.add(new BarDataPoint(time, basal = basebasal, false)); - } - if (basebasal != lastBaseBasal) - basalLineArray.add(new DataPoint(time, lastBaseBasal)); - basalLineArray.add(new DataPoint(time, basebasal)); - lastBaseBasal = basebasal; - maxBasalValueFound = Math.max(maxBasalValueFound, basal); - } - BarDataPoint[] basal = new BarDataPoint[basalArray.size()]; - basal = basalArray.toArray(basal); - bgGraph.addSeries(basalsSeries = new BarGraphSeries(basal)); - basalsSeries.setValueDependentColor(new ValueDependentColor() { - @Override - public int get(DataPoint data) { - BarDataPoint point = (BarDataPoint) data; - if (point.isTempBasal) return Color.BLUE; - else return Color.CYAN; - } - }); - DataPoint[] basalLine = new DataPoint[basalLineArray.size()]; - basalLine = basalLineArray.toArray(basalLine); - bgGraph.addSeries(basalsLineSeries = new LineGraphSeries(basalLine)); - basalsLineSeries.setColor(Color.CYAN); - basalsLineSeries.setDrawDataPoints(false); - basalsLineSeries.setThickness(2); - } - // set manual x bounds to have nice steps bgGraph.getViewport().setMaxX(endTime); bgGraph.getViewport().setMinX(fromTime); @@ -1110,8 +1132,9 @@ public class OverviewFragment extends Fragment { bgGraph.getGridLabelRenderer().setNumVerticalLabels(numOfHorizLines); // set second scale - if (pump.getPumpDescription().isTempBasalCapable) { - bgGraph.getSecondScale().addSeries(basalsSeries); + if (pump.getPumpDescription().isTempBasalCapable && showBasalsView.isChecked()) { + bgGraph.getSecondScale().addSeries(baseBasalsSeries); + bgGraph.getSecondScale().addSeries(tempBasalsSeries); bgGraph.getSecondScale().addSeries(basalsLineSeries); bgGraph.getSecondScale().setMinY(0); bgGraph.getSecondScale().setMaxY(maxBgValue / lowLine * maxBasalValueFound * 1.2d); diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index 26d53c7e9a..262775daa3 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -1,4 +1,5 @@ + + + android:layout_alignWithParentIfMissing="false" + android:layout_below="@+id/overview_showprediction" + app:buttonTint="@color/cyan" />