diff --git a/app/build.gradle b/app/build.gradle index 15015ca255..596ca090e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,8 +49,8 @@ def generateGitBuild = { -> return stringBuilder.toString() } -tasks.matching {it instanceof Test}.all { - testLogging.events = ["failed", "skipped"] +tasks.matching { it instanceof Test }.all { + testLogging.events = ["failed", "skipped", "started"] testLogging.exceptionFormat = "full" } @@ -89,7 +89,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { - testCoverageEnabled (project.hasProperty('coverage') ? true : false) + testCoverageEnabled(project.hasProperty('coverage') ? true : false) } } productFlavors { @@ -156,7 +156,7 @@ android { unitTests.returnDefaultValues = true unitTests.includeAndroidResources = true } - } +} allprojects { repositories { @@ -172,6 +172,7 @@ configurations { } dependencies { + implementation 'com.android.support:support-v4:27.1.1' wearApp project(':wear') implementation fileTree(include: ['*.jar'], dir: 'libs') @@ -242,7 +243,7 @@ dependencies { } task unzip(type: Copy) { - def zipPath = configurations.libs.find {it.name.startsWith("danars") } + def zipPath = configurations.libs.find { it.name.startsWith("danars") } def zipFile = file(zipPath) def outputDir = file("${buildDir}/unpacked/dist") @@ -263,4 +264,4 @@ task full_clean(type: Delete) { } clean.dependsOn full_clean -preBuild.dependsOn copyLibs \ No newline at end of file +preBuild.dependsOn copyLibs diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 00d59f09bb..a65f92d7b3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,12 +51,14 @@ + + @@ -68,6 +70,7 @@ android:enabled="true" android:exported="true"> + @@ -130,7 +133,7 @@ + android:exported="true" /> @@ -148,12 +151,17 @@ android:exported="true" /> + android:exported="false" /> + \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index ff7b935355..afb40421df 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -37,6 +37,7 @@ public class Config { public static final boolean logCongigBuilderActions = true; public static final boolean logAutosensData = false; public static final boolean logEvents = false; + public static final boolean logProfile = false; // DanaR specific public static final boolean logDanaBTComm = true; diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 440024f853..a902ca7091 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -49,6 +49,7 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Food.FoodPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.androidaps.startupwizard.SetupWizardActivity; import info.nightscout.androidaps.tabs.SlidingTabLayout; import info.nightscout.androidaps.tabs.TabPageAdapter; import info.nightscout.utils.ImportExportPrefs; @@ -368,6 +369,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe case R.id.nav_historybrowser: startActivity(new Intent(v.getContext(), HistoryBrowseActivity.class)); break; + case R.id.nav_setupwizard: + startActivity(new Intent(v.getContext(), SetupWizardActivity.class)); + break; case R.id.nav_resetdb: new AlertDialog.Builder(v.getContext()) .setTitle(R.string.nav_resetdb) 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 6a771fc4a1..3e0bbf9c74 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -197,7 +197,8 @@ public class DataService extends IntentService { bgReading.direction = bundle.getString(Intents.EXTRA_BG_SLOPE_NAME); bgReading.date = bundle.getLong(Intents.EXTRA_TIMESTAMP); bgReading.raw = bundle.getDouble(Intents.EXTRA_RAW); - + String source = bundle.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION, "no Source specified"); + SourceXdripPlugin.getPlugin().setSource(source); MainApp.getDbHelper().createIfNotExists(bgReading, "XDRIP"); } diff --git a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java index b9c90e905c..744530c8f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java @@ -37,6 +37,8 @@ public interface Intents { String EXTRA_SENSOR_BATTERY = "com.eveningoutpost.dexdrip.Extras.SensorBattery"; String EXTRA_TIMESTAMP = "com.eveningoutpost.dexdrip.Extras.Time"; String EXTRA_RAW = "com.eveningoutpost.dexdrip.Extras.Raw"; + String XDRIP_DATA_SOURCE_DESCRIPTION = "com.eveningoutpost.dexdrip.Extras.SourceDesc"; + String ACTION_NEW_BG_ESTIMATE_NO_DATA = "com.eveningoutpost.dexdrip.BgEstimateNoData"; diff --git a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java index 3935b17b8a..c2f9d16dc3 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java +++ b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java @@ -30,6 +30,7 @@ public class DetailedBolusInfo { public long pumpId = 0; // id of record if comming from pump history (not a newly created treatment) public boolean isSMB = false; // is a Super-MicroBolus public long deliverAt = 0; // SMB should be delivered within 1 min from this time + public String notes = null; public DetailedBolusInfo copy() { DetailedBolusInfo n = new DetailedBolusInfo(); @@ -47,6 +48,7 @@ public class DetailedBolusInfo { n.pumpId = pumpId; n.isSMB = isSMB; n.deliverAt = deliverAt; + n.notes = notes; return n; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index e966c4d11d..783124168f 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -12,6 +12,7 @@ import java.text.DecimalFormat; import java.util.Calendar; import java.util.TimeZone; +import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -275,7 +276,7 @@ public class Profile { Integer getShitfTimeSecs(Integer originalTime) { Integer shiftedTime = originalTime + timeshift * 60 * 60; shiftedTime = (shiftedTime + 24 * 60 * 60) % (24 * 60 * 60); - if (timeshift != 0) + if (timeshift != 0 && Config.logProfile) log.debug("(Sec) Original time: " + originalTime + " ShiftedTime: " + shiftedTime); return shiftedTime; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java index 4a94db0a44..e4cd50c490 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java +++ b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java @@ -11,7 +11,6 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; @@ -119,8 +118,8 @@ public class QuickWizardEntry { if (useSuperBolus() == YES && SP.getBoolean(R.string.key_usesuperbolus, false)) { superBolus = true; } - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isSuperBolus()) + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if (loopPlugin.isEnabled(loopPlugin.getType()) && loopPlugin.isSuperBolus()) superBolus = false; // Trend 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 75e830e654..7174c19ec4 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -28,6 +28,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; @@ -45,6 +46,7 @@ import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistor import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRNSHistorySync; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.utils.JsonHelper; import info.nightscout.utils.NSUpload; import info.nightscout.utils.PercentageSplitter; import info.nightscout.utils.ToastUtils; @@ -662,7 +664,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void createTemptargetFromJsonIfNotExists(JSONObject trJson) { try { - String units = MainApp.getConfigBuilder().getProfileUnits(); + String units = JsonHelper.safeGetString(trJson, "units", MainApp.getConfigBuilder().getProfileUnits()); TempTarget tempTarget = new TempTarget() .date(trJson.getLong("mills")) .duration(trJson.getInt("duration")) diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 30610ca2f6..27a6a0846b 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -55,6 +55,7 @@ public interface TreatmentsInterface { TempTarget getTempTargetFromHistory(); TempTarget getTempTargetFromHistory(long time); Intervals getTempTargetsFromHistory(); + void addToHistoryTempTarget(TempTarget tempTarget); ProfileSwitch getProfileSwitchFromHistory(long time); ProfileIntervals getProfileSwitchesFromHistory(); 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 b3b15bf7fb..5e99a6ca17 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 @@ -5,7 +5,9 @@ import android.content.Intent; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; +import android.text.Editable; import android.text.Html; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -14,6 +16,8 @@ import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.LinearLayout; import com.crashlytics.android.answers.CustomEvent; import com.google.common.base.Joiner; @@ -21,8 +25,6 @@ import com.google.common.base.Joiner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.Constants; -import java.text.DecimalFormat; import java.util.LinkedList; import java.util.List; @@ -41,19 +43,45 @@ import info.nightscout.utils.NSUpload; import info.nightscout.utils.NumberPicker; import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; +import info.nightscout.utils.ToastUtils; + +import static info.nightscout.utils.DateUtil.now; public class FillDialog extends DialogFragment implements OnClickListener { private static Logger log = LoggerFactory.getLogger(FillDialog.class); + private CheckBox pumpSiteChangeCheckbox; + private CheckBox insulinCartridgeChangeCheckbox; + + private NumberPicker editInsulin; + double amount1 = 0d; double amount2 = 0d; double amount3 = 0d; - NumberPicker editInsulin; - CheckBox pumpSiteChangeCheckbox; - CheckBox insulinCartridgeChangeCheckbox; + private EditText notesEdit; - public FillDialog() { + final private TextWatcher textWatcher = new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + validateInputs(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }; + + private void validateInputs() { + int time = editInsulin.getValue().intValue(); + if (Math.abs(time) > 12 * 60) { + editInsulin.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); + } } @Override @@ -67,45 +95,47 @@ public class FillDialog extends DialogFragment implements OnClickListener { getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - pumpSiteChangeCheckbox = view.findViewById(R.id.catheter_change); - insulinCartridgeChangeCheckbox = view.findViewById(R.id.cartridge_change); + pumpSiteChangeCheckbox = view.findViewById(R.id.fill_catheter_change); + insulinCartridgeChangeCheckbox = view.findViewById(R.id.fill_cartridge_change); Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value(); double bolusstep = ConfigBuilderPlugin.getActivePump().getPumpDescription().bolusStep; - editInsulin = view.findViewById(R.id.treatments_newtreatment_insulinamount); - editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false); + editInsulin = view.findViewById(R.id.fill_insulinamount); + editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher); - //setup preset buttons - Button button1 = (Button) view.findViewById(R.id.fill_preset_button1); - Button button2 = (Button) view.findViewById(R.id.fill_preset_button2); - Button button3 = (Button) view.findViewById(R.id.fill_preset_button3); + Button preset1Button = view.findViewById(R.id.fill_preset_button1); amount1 = SP.getDouble("fill_button1", 0.3); - amount2 = SP.getDouble("fill_button2", 0d); - amount3 = SP.getDouble("fill_button3", 0d); - if (amount1 > 0) { - button1.setVisibility(View.VISIBLE); - button1.setText(DecimalFormatter.toPumpSupportedBolus(amount1)); // + "U"); - button1.setOnClickListener(this); + preset1Button.setVisibility(View.VISIBLE); + preset1Button.setText(DecimalFormatter.toPumpSupportedBolus(amount1)); // + "U"); + preset1Button.setOnClickListener(this); } else { - button1.setVisibility(View.GONE); + preset1Button.setVisibility(View.GONE); } + Button preset2Button = view.findViewById(R.id.fill_preset_button2); + amount2 = SP.getDouble("fill_button2", 0d); if (amount2 > 0) { - button2.setVisibility(View.VISIBLE); - button2.setText(DecimalFormatter.toPumpSupportedBolus(amount2)); // + "U"); - button2.setOnClickListener(this); + preset2Button.setVisibility(View.VISIBLE); + preset2Button.setText(DecimalFormatter.toPumpSupportedBolus(amount2)); // + "U"); + preset2Button.setOnClickListener(this); } else { - button2.setVisibility(View.GONE); + preset2Button.setVisibility(View.GONE); } + Button preset3Button = view.findViewById(R.id.fill_preset_button3); + amount3 = SP.getDouble("fill_button3", 0d); if (amount3 > 0) { - button3.setVisibility(View.VISIBLE); - button3.setText(DecimalFormatter.toPumpSupportedBolus(amount3)); // + "U"); - button3.setOnClickListener(this); + preset3Button.setVisibility(View.VISIBLE); + preset3Button.setText(DecimalFormatter.toPumpSupportedBolus(amount3)); // + "U"); + preset3Button.setOnClickListener(this); } else { - button3.setVisibility(View.GONE); + preset3Button.setVisibility(View.GONE); } + LinearLayout notesLayout = view.findViewById(R.id.fill_notes_layout); + notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); + notesEdit = view.findViewById(R.id.fill_notes); + setCancelable(true); getDialog().setCanceledOnTouchOutside(false); return view; @@ -154,42 +184,50 @@ public class FillDialog extends DialogFragment implements OnClickListener { if (insulinCartridgeChangeCheckbox.isChecked()) confirmMessage.add("" + "" + getString(R.string.record_insulin_cartridge_change) + ""); + final String notes = notesEdit.getText().toString(); + if (!notes.isEmpty()) { + confirmMessage.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); + } + final Double finalInsulinAfterConstraints = insulinAfterConstraints; final Context context = getContext(); AlertDialog.Builder builder = new AlertDialog.Builder(context); - if (confirmMessage.isEmpty()) - confirmMessage.add(MainApp.gs(R.string.no_action_selected)); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(Html.fromHtml(Joiner.on("
").join(confirmMessage))); - builder.setPositiveButton(getString(R.string.primefill), (dialog, id) -> { - if (finalInsulinAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.isValid = false; // do not count it in IOB (for pump history) - ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.sResources.getString(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); + if (insulinAfterConstraints > 0 || pumpSiteChangeCheckbox.isChecked() || insulinCartridgeChangeCheckbox.isChecked()) { + builder.setMessage(Html.fromHtml(Joiner.on("
").join(confirmMessage))); + builder.setPositiveButton(getString(R.string.primefill), (dialog, id) -> { + if (finalInsulinAfterConstraints > 0) { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.insulin = finalInsulinAfterConstraints; + detailedBolusInfo.context = context; + detailedBolusInfo.source = Source.USER; + detailedBolusInfo.isValid = false; // do not count it in IOB (for pump history) + detailedBolusInfo.notes = notes; + ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.sResources.getString(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } } - } - }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("Fill")); - } - long now = System.currentTimeMillis(); - if (pumpSiteChangeCheckbox.isChecked()) NSUpload.uploadEvent(CareportalEvent.SITECHANGE, now); - if (insulinCartridgeChangeCheckbox.isChecked()) NSUpload.uploadEvent(CareportalEvent.INSULINCHANGE, now + 1000); - }); + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("Fill")); + } + if (pumpSiteChangeCheckbox.isChecked()) + NSUpload.uploadEvent(CareportalEvent.SITECHANGE, now(), notes); + if (insulinCartridgeChangeCheckbox.isChecked()) + NSUpload.uploadEvent(CareportalEvent.INSULINCHANGE, now() + 1000, notes); + }); + } else { + builder.setMessage(MainApp.gs(R.string.no_action_selected)); + } builder.setNegativeButton(getString(R.string.cancel), null); builder.show(); dismiss(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java index 61122fb0c2..56aa0158cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java @@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.Careportal.Dialogs; import android.app.Activity; -import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; @@ -15,7 +14,6 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RadioButton; @@ -23,6 +21,7 @@ import android.widget.Spinner; import android.widget.TextView; import com.crashlytics.android.answers.CustomEvent; +import com.google.common.collect.Lists; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout; import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; @@ -52,8 +51,10 @@ import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.plugins.Careportal.OptionsToShow; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.DefaultValueHelper; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.HardLimits; +import info.nightscout.utils.JsonHelper; import info.nightscout.utils.NSUpload; import info.nightscout.utils.NumberPicker; import info.nightscout.utils.SP; @@ -69,7 +70,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick private static String event; Profile profile; - ProfileStore profileStore; + public ProfileStore profileStore; String units = Constants.MGDL; TextView eventTypeText; @@ -108,14 +109,14 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick public void setOptions(OptionsToShow options, int event) { this.options = options; - this.event = MainApp.sResources.getString(event); + this.event = MainApp.gs(event); } public NewNSTreatmentDialog() { super(); if (seconds == null) { - seconds = new Double(Math.random() * 59).intValue(); + seconds = Double.valueOf(Math.random() * 59).intValue(); } } @@ -135,7 +136,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (options == null) return null; - getDialog().setTitle(getString(options.eventName)); + getDialog().setTitle(MainApp.gs(options.eventName)); setStyle(DialogFragment.STYLE_NORMAL, getTheme()); View view = inflater.inflate(R.layout.careportal_newnstreatment_dialog, container, false); @@ -192,36 +193,40 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick final Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units); // temp target - final ArrayList reasonList = new ArrayList(); - reasonList.add(MainApp.sResources.getString(R.string.manual)); - reasonList.add(MainApp.sResources.getString(R.string.eatingsoon)); - reasonList.add(MainApp.sResources.getString(R.string.activity)); - ArrayAdapter adapterReason = new ArrayAdapter(getContext(), + final List reasonList = Lists.newArrayList( + MainApp.gs(R.string.manual), + MainApp.gs(R.string.eatingsoon), + MainApp.gs(R.string.activity), + MainApp.gs(R.string.hypo)); + ArrayAdapter adapterReason = new ArrayAdapter<>(getContext(), R.layout.spinner_centered, reasonList); reasonSpinner.setAdapter(adapterReason); reasonSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - double defaultDuration = 0; + double defaultDuration; double defaultTarget = 0; if (profile != null) { - defaultTarget = bg.doubleValue(); + defaultTarget = bg; } boolean erase = false; - if (MainApp.sResources.getString(R.string.eatingsoon).equals(reasonList.get(position))) { - defaultDuration = SP.getDouble(R.string.key_eatingsoon_duration, 0d); - defaultTarget = SP.getDouble(R.string.key_eatingsoon_target, 0d); - ; - } else if (MainApp.sResources.getString(R.string.activity).equals(reasonList.get(position))) { - defaultDuration = SP.getDouble(R.string.key_activity_duration, 0d); - ; - defaultTarget = SP.getDouble(R.string.key_activity_target, 0d); - ; + String units = MainApp.getConfigBuilder().getProfileUnits(); + DefaultValueHelper helper = new DefaultValueHelper(); + if (MainApp.gs(R.string.eatingsoon).equals(reasonList.get(position))) { + defaultDuration = helper.determineEatingSoonTTDuration(); + defaultTarget = helper.determineEatingSoonTT(units); + } else if (MainApp.gs(R.string.activity).equals(reasonList.get(position))) { + defaultDuration = helper.determineActivityTTDuration(); + defaultTarget = helper.determineActivityTT(units); + } else if (MainApp.gs(R.string.hypo).equals(reasonList.get(position))) { + defaultDuration = helper.determineHypoTTDuration(); + defaultTarget = helper.determineHypoTT(units); } else { defaultDuration = 0; erase = true; } + if (defaultTarget != 0 || erase) { editTemptarget.setValue(defaultTarget); } @@ -266,12 +271,9 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick editBg.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, bgTextWatcher); editTemptarget.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false); } - sensorRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, profile.getUnits()); - editBg.setValue(bg); - } + sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> { + Double bg1 = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, profile.getUnits()); + editBg.setValue(bg1); }); Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); @@ -407,7 +409,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick tpd.show(context.getFragmentManager(), "Timepickerdialog"); break; case R.id.ok: - createNSTreatment(); + confirmNSTreatmentCreation(); dismiss(); break; case R.id.cancel: @@ -571,158 +573,151 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick String buildConfirmText(JSONObject data) { String ret = ""; - try { - if (data.has("eventType")) { - ret += getString(R.string.careportal_newnstreatment_eventtype); - ret += ": "; - ret += Translator.translate(data.getString("eventType")); - ret += "\n"; - } - if (data.has("glucose")) { - ret += getString(R.string.treatments_wizard_bg_label); - ret += ": "; - ret += data.get("glucose"); - ret += " " + units + "\n"; - } - if (data.has("glucoseType")) { - ret += getString(R.string.careportal_newnstreatment_glucosetype); - ret += ": "; - ret += Translator.translate(data.getString("glucoseType")); - ret += "\n"; - } - if (data.has("carbs")) { - ret += getString(R.string.careportal_newnstreatment_carbs_label); - ret += ": "; - ret += data.get("carbs"); - ret += " g\n"; - } - if (data.has("insulin")) { - ret += getString(R.string.careportal_newnstreatment_insulin_label); - ret += ": "; - ret += data.get("insulin"); - ret += " U\n"; - } - if (data.has("duration")) { - ret += getString(R.string.careportal_newnstreatment_duration_label); - ret += ": "; - ret += data.get("duration"); - ret += " min\n"; - } - if (data.has("percent")) { - ret += getString(R.string.careportal_newnstreatment_percent_label); - ret += ": "; - ret += data.get("percent"); - ret += " %\n"; - } - if (data.has("absolute")) { - ret += getString(R.string.careportal_newnstreatment_absolute_label); - ret += ": "; - ret += data.get("absolute"); - ret += " U/h\n"; - } - if (data.has("preBolus")) { - ret += getString(R.string.careportal_newnstreatment_carbtime_label); - ret += ": "; - ret += data.get("preBolus"); - ret += " min\n"; - } - if (data.has("notes")) { - ret += getString(R.string.careportal_newnstreatment_notes_label); - ret += ": "; - ret += data.get("notes"); - ret += "\n"; - } - if (data.has("profile")) { - ret += getString(R.string.careportal_newnstreatment_profile_label); - ret += ": "; - ret += data.get("profile"); - ret += "\n"; - } - if (data.has("percentage")) { - ret += getString(R.string.careportal_newnstreatment_percentage_label); - ret += ": "; - ret += data.get("percentage"); - ret += " %\n"; - } - if (data.has("timeshift")) { - ret += getString(R.string.careportal_newnstreatment_timeshift_label); - ret += ": "; - ret += data.get("timeshift"); - ret += " h\n"; - } - if (data.has("targetBottom") && data.has("targetTop")) { - ret += getString(R.string.target_range); - ret += " "; - ret += data.get("targetBottom"); - ret += " - "; - ret += data.get("targetTop"); - ret += "\n"; - } - if (data.has("created_at")) { - ret += getString(R.string.careportal_newnstreatment_eventtime_label); - ret += ": "; - ret += eventTime.toLocaleString(); - ret += "\n"; - } - if (data.has("enteredBy")) { - ret += getString(R.string.careportal_newnstreatment_enteredby_title); - ret += ": "; - ret += data.get("enteredBy"); - ret += "\n"; - } - } catch (JSONException e) { - log.error("Unhandled exception", e); + if (data.has("eventType")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_eventtype); + ret += ": "; + ret += Translator.translate(JsonHelper.safeGetString(data, "eventType", "")); + ret += "\n"; + } + if (data.has("glucose")) { + ret += MainApp.gs(R.string.treatments_wizard_bg_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "glucose", ""); + ret += " " + units + "\n"; + } + if (data.has("glucoseType")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_glucosetype); + ret += ": "; + ret += Translator.translate(JsonHelper.safeGetString(data, "glucoseType", "")); + ret += "\n"; + } + if (data.has("carbs")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_carbs_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "carbs", ""); + ret += " g\n"; + } + if (data.has("insulin")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_insulin_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "insulin", ""); + ret += " U\n"; + } + if (data.has("duration")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_duration_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "duration", ""); + ret += " min\n"; + } + if (data.has("percent")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_percent_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "percent", ""); + ret += " %\n"; + } + if (data.has("absolute")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_absolute_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "absolute", ""); + ret += " U/h\n"; + } + if (data.has("preBolus")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_carbtime_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "preBolus", ""); + ret += " min\n"; + } + if (data.has("notes")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_notes_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "notes", ""); + ret += "\n"; + } + if (data.has("profile")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_profile_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "profile", ""); + ret += "\n"; + } + if (data.has("percentage")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_percentage_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "percentage", ""); + ret += " %\n"; + } + if (data.has("timeshift")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_timeshift_label); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "timeshift", ""); + ret += " h\n"; + } + if (data.has("targetBottom") && data.has("targetTop")) { + ret += MainApp.gs(R.string.target_range); + ret += " "; + ret += JsonHelper.safeGetObject(data, "targetBottom", ""); + ret += " - "; + ret += JsonHelper.safeGetObject(data, "targetTop", ""); + ret += "\n"; + } + if (data.has("created_at")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_eventtime_label); + ret += ": "; + ret += eventTime.toLocaleString(); + ret += "\n"; + } + if (data.has("enteredBy")) { + ret += MainApp.gs(R.string.careportal_newnstreatment_enteredby_title); + ret += ": "; + ret += JsonHelper.safeGetObject(data, "enteredBy", ""); + ret += "\n"; } return ret; } - void createNSTreatment() { - final JSONObject data = gatherData(); - String confirmText = buildConfirmText(data); - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(getContext().getString(R.string.confirmation)); - builder.setMessage(confirmText); - builder.setPositiveButton(getContext().getString(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - if (options.executeProfileSwitch) { - if (data.has("profile")) { - try { - doProfileSwitch(profileStore, data.getString("profile"), data.getInt("duration"), data.getInt("percentage"), data.getInt("timeshift")); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - } else if (options.executeTempTarget) { - try { - if ((data.has("targetBottom") && data.has("targetTop")) || (data.has("duration") && data.getInt("duration") == 0)) { - TempTarget tempTarget = new TempTarget() - .date(eventTime.getTime()) - .duration(data.getInt("duration")) - .reason(data.getString("reason")) - .source(Source.USER); - if (tempTarget.durationInMinutes != 0) { - tempTarget.low(Profile.toMgdl(data.getDouble("targetBottom"), profile.getUnits())) - .high(Profile.toMgdl(data.getDouble("targetTop"), profile.getUnits())); - } else { - tempTarget.low(0).high(0); - } - log.debug("Creating new TempTarget db record: " + tempTarget.toString()); - MainApp.getDbHelper().createOrUpdate(tempTarget); - NSUpload.uploadCareportalEntryToNS(data); - FabricPrivacy.getInstance().logCustom(new CustomEvent("TempTarget")); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } else { - NSUpload.uploadCareportalEntryToNS(data); - FabricPrivacy.getInstance().logCustom(new CustomEvent("NSTreatment")); - } + void confirmNSTreatmentCreation() { + if (context != null) { + final JSONObject data = gatherData(); + final String confirmText = buildConfirmText(data); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(MainApp.gs(R.string.confirmation)); + builder.setMessage(confirmText); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> createNSTreatment(data)); + builder.setNegativeButton(MainApp.gs(R.string.cancel), null); + builder.show(); + } + } + + + public void createNSTreatment(JSONObject data) { + if (options.executeProfileSwitch) { + if (data.has("profile")) { + doProfileSwitch(profileStore, JsonHelper.safeGetString(data, "profile"), JsonHelper.safeGetInt(data, "duration"), JsonHelper.safeGetInt(data, "percentage"), JsonHelper.safeGetInt(data, "timeshift")); } - }); - builder.setNegativeButton(getContext().getString(R.string.cancel), null); - builder.show(); + } else if (options.executeTempTarget) { + final int duration = JsonHelper.safeGetInt(data, "duration"); + final double targetBottom = JsonHelper.safeGetDouble(data, "targetBottom"); + final double targetTop = JsonHelper.safeGetDouble(data, "targetTop"); + final String reason = JsonHelper.safeGetString(data, "reason", ""); + if ((targetBottom != 0d && targetTop != 0d) || duration == 0) { + TempTarget tempTarget = new TempTarget() + .date(eventTime.getTime()) + .duration(duration) + .reason(reason) + .source(Source.USER); + if (tempTarget.durationInMinutes != 0) { + tempTarget.low(Profile.toMgdl(targetBottom, profile.getUnits())) + .high(Profile.toMgdl(targetTop, profile.getUnits())); + } else { + tempTarget.low(0).high(0); + } + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + FabricPrivacy.getInstance().logCustom(new CustomEvent("TempTarget")); + } + } else { + NSUpload.uploadCareportalEntryToNS(data); + FabricPrivacy.getInstance().logCustom(new CustomEvent("NSTreatment")); + } } public static void doProfileSwitch(final ProfileStore profileStore, final String profileName, final int duration, final int percentage, final int timeshift) { 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 c6b2e3ce96..06ddbef86f 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 @@ -264,10 +264,6 @@ public class ConfigBuilderPlugin extends PluginBase { return activeAPS; } - public static LoopPlugin getActiveLoop() { - return activeLoop; - } - public static PumpInterface getActivePump() { return activePump; } @@ -621,7 +617,7 @@ public class ConfigBuilderPlugin extends PluginBase { } public void disconnectPump(int durationInMinutes, Profile profile) { - getActiveLoop().disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L); + LoopPlugin.getPlugin().disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L); getCommandQueue().tempBasalPercent(0, durationInMinutes, true, profile, new Callback() { @Override public void run() { @@ -644,7 +640,7 @@ public class ConfigBuilderPlugin extends PluginBase { } public void suspendLoop(int durationInMinutes) { - getActiveLoop().suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000); + LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000); getCommandQueue().cancelTempBasal(true, new Callback() { @Override public void run() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java index 23f5cb1fb8..688c741329 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java @@ -85,37 +85,52 @@ public class ObjectivesFragment extends SubscriberFragment { } }); - Long now = System.currentTimeMillis(); - if (position > 0 && objectives.get(position - 1).accomplished.getTime() == 0) { - // Phase 0: previous not completed - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - } else if (o.started.getTime() == 0) { - // Phase 1: not started - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - holder.started.setVisibility(View.GONE); - } else if (o.started.getTime() > 0 && !enableFake.isChecked() && o.accomplished.getTime() == 0 && !(o.started.getTime() + o.durationInDays * 24 * 60 * 60 * 1000 < now && requirementsMet.done)) { - // Phase 2: started, waiting for duration and met requirements - holder.startButton.setEnabled(false); - holder.verifyLayout.setVisibility(View.GONE); - } else if (o.accomplished.getTime() == 0) { - // Phase 3: started, after duration, requirements met - holder.startButton.setEnabled(false); - holder.accomplished.setVisibility(View.INVISIBLE); - } else { - // Phase 4: verified - holder.gateLayout.setVisibility(View.GONE); - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyButton.setVisibility(View.INVISIBLE); + long prevObjectiveAccomplishedTime = position > 0 ? + objectives.get(position - 1).accomplished.getTime() : -1; + + int phase = modifyVisibility(position, prevObjectiveAccomplishedTime, + o.started.getTime(), o.durationInDays, + o.accomplished.getTime(), requirementsMet.done, enableFake.isChecked()); + + switch (phase) { + case 0: + // Phase 0: previous not completed + holder.startedLayout.setVisibility(View.GONE); + holder.durationLayout.setVisibility(View.GONE); + holder.progressLayout.setVisibility(View.GONE); + holder.verifyLayout.setVisibility(View.GONE); + break; + case 1: + // Phase 1: not started + holder.durationLayout.setVisibility(View.GONE); + holder.progressLayout.setVisibility(View.GONE); + holder.verifyLayout.setVisibility(View.GONE); + holder.started.setVisibility(View.GONE); + break; + case 2: + // Phase 2: started, waiting for duration and met requirements + holder.startButton.setEnabled(false); + holder.verifyLayout.setVisibility(View.GONE); + break; + case 3: + // Phase 3: started, after duration, requirements met + holder.startButton.setEnabled(false); + holder.accomplished.setVisibility(View.INVISIBLE); + break; + case 4: + // Phase 4: verified + holder.gateLayout.setVisibility(View.GONE); + holder.startedLayout.setVisibility(View.GONE); + holder.durationLayout.setVisibility(View.GONE); + holder.progressLayout.setVisibility(View.GONE); + holder.verifyButton.setVisibility(View.INVISIBLE); + break; + default: + // should not happen } } + @Override public int getItemCount() { return objectives.size(); @@ -164,6 +179,40 @@ public class ObjectivesFragment extends SubscriberFragment { } } + /** + * returns an int, which represents the phase the current objective is at. + * + * this is mainly used for unit-testing the conditions + * + * @param currentPosition + * @param prevObjectiveAccomplishedTime + * @param objectiveStartedTime + * @param durationInDays + * @param objectiveAccomplishedTime + * @param requirementsMet + * @return + */ + public int modifyVisibility(int currentPosition, + long prevObjectiveAccomplishedTime, + long objectiveStartedTime, int durationInDays, + long objectiveAccomplishedTime, boolean requirementsMet, + boolean enableFakeValue) { + Long now = System.currentTimeMillis(); + if (currentPosition > 0 && prevObjectiveAccomplishedTime == 0) { + return 0; + } else if (objectiveStartedTime == 0) { + return 1; + } else if (objectiveStartedTime > 0 && !enableFakeValue + && objectiveAccomplishedTime == 0 + && !(objectiveStartedTime + durationInDays * 24 * 60 * 60 * 1000 >= now && requirementsMet)) { + return 2; + } else if (objectiveAccomplishedTime == 0) { + return 3; + } else { + return 4; + } + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -205,6 +254,7 @@ public class ObjectivesFragment extends SubscriberFragment { ObjectivesPlugin.objectives.get(3).gate = MainApp.sResources.getString(R.string.objectives_3_gate); ObjectivesPlugin.objectives.get(4).gate = MainApp.sResources.getString(R.string.objectives_4_gate); ObjectivesPlugin.objectives.get(5).gate = MainApp.sResources.getString(R.string.objectives_5_gate); + updateGUI(); return view; @@ -225,4 +275,4 @@ public class ObjectivesFragment extends SubscriberFragment { }); } -} +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index e29eda04dc..e5510aae00 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -9,6 +9,7 @@ import android.app.TaskStackBuilder; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.support.annotation.NonNull; import android.support.v4.app.NotificationCompat; import com.crashlytics.android.answers.CustomEvent; @@ -56,6 +57,7 @@ public class LoopPlugin extends PluginBase { protected static LoopPlugin loopPlugin; + @NonNull public static LoopPlugin getPlugin() { if (loopPlugin == null) { loopPlugin = new LoopPlugin(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java index 19119450df..bd489971c2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java @@ -1,15 +1,14 @@ package info.nightscout.androidaps.plugins.Overview.Dialogs; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.HandlerThread; +import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.Html; import android.text.TextWatcher; -import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -18,20 +17,16 @@ import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.LinearLayout; import android.widget.RadioButton; -import android.widget.TextView; import com.google.common.base.Joiner; -import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; -import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout; -import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.DecimalFormat; -import java.util.Calendar; -import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -49,36 +44,32 @@ import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; +import info.nightscout.utils.DefaultValueHelper; import info.nightscout.utils.NumberPicker; import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; import info.nightscout.utils.ToastUtils; -public class NewCarbsDialog extends DialogFragment implements OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener, CompoundButton.OnCheckedChangeListener { +import static info.nightscout.utils.DateUtil.now; + +public class NewCarbsDialog extends DialogFragment implements OnClickListener, CompoundButton.OnCheckedChangeListener { private static Logger log = LoggerFactory.getLogger(NewCarbsDialog.class); - private NumberPicker editCarbs; - - private TextView dateButton; - private TextView timeButton; - - private Date initialEventTime; - private Date eventTime; - - private Button fav1Button; - private Button fav2Button; - private Button fav3Button; - private static final int FAV1_DEFAULT = 5; private static final int FAV2_DEFAULT = 10; private static final int FAV3_DEFAULT = 20; + private RadioButton startActivityTTCheckbox; private RadioButton startEatingSoonTTCheckbox; private RadioButton startHypoTTCheckbox; private boolean togglingTT; + private NumberPicker editTime; + private NumberPicker editDuration; + private NumberPicker editCarbs; private Integer maxCarbs; + private EditText notesEdit; + //one shot guards private boolean accepted; private boolean okClicked; @@ -91,6 +82,7 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D final private TextWatcher textWatcher = new TextWatcher() { @Override public void afterTextChanged(Editable s) { + validateInputs(); } @Override @@ -99,12 +91,21 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - validateInputs(); } }; private void validateInputs() { - Integer carbs = SafeParse.stringToInt(editCarbs.getText()); + int time = editTime.getValue().intValue(); + if (time > 12 * 60 || time < -12 * 60) { + editTime.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); + } + Double duration = editDuration.getValue(); + if (duration > 10) { + editDuration.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); + } + int carbs = editCarbs.getValue().intValue(); if (carbs > maxCarbs) { editCarbs.setValue(0d); ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.carbsconstraintapplied)); @@ -122,12 +123,6 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); - - editCarbs = view.findViewById(R.id.newcarb_carbsamount); - - editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher); - startActivityTTCheckbox = view.findViewById(R.id.newcarbs_activity_tt); startActivityTTCheckbox.setOnCheckedChangeListener(this); startEatingSoonTTCheckbox = view.findViewById(R.id.newcarbs_eating_soon_tt); @@ -135,28 +130,36 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt); startHypoTTCheckbox.setOnCheckedChangeListener(this); - dateButton = view.findViewById(R.id.newcarbs_eventdate); - timeButton = view.findViewById(R.id.newcarb_eventtime); + editTime = view.findViewById(R.id.newcarbs_time); + editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, textWatcher); - initialEventTime = new Date(); - eventTime = new Date(initialEventTime.getTime()); - dateButton.setText(DateUtil.dateString(eventTime)); - timeButton.setText(DateUtil.timeString(eventTime)); - dateButton.setOnClickListener(this); - timeButton.setOnClickListener(this); + LinearLayout durationLayout = view.findViewById(R.id.newcarbs_duration_layout); + durationLayout.setVisibility(MainApp.engineeringMode ? View.VISIBLE : View.GONE); - fav1Button = view.findViewById(R.id.newcarbs_plus1); + editDuration = view.findViewById(R.id.new_carbs_duration); + editDuration.setParams(0d, 0d, 10d, 1d, new DecimalFormat("0"), false, textWatcher); + + maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); + + editCarbs = view.findViewById(R.id.newcarb_carbsamount); + editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher); + + Button fav1Button = view.findViewById(R.id.newcarbs_plus1); fav1Button.setOnClickListener(this); fav1Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))); - fav2Button = view.findViewById(R.id.newcarbs_plus2); + Button fav2Button = view.findViewById(R.id.newcarbs_plus2); fav2Button.setOnClickListener(this); fav2Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))); - fav3Button = view.findViewById(R.id.newcarbs_plus3); + Button fav3Button = view.findViewById(R.id.newcarbs_plus3); fav3Button.setOnClickListener(this); fav3Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))); + LinearLayout notesLayout = view.findViewById(R.id.newcarbs_notes_layout); + notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); + notesEdit = view.findViewById(R.id.newcarbs_notes); + setCancelable(true); getDialog().setCanceledOnTouchOutside(false); return view; @@ -168,8 +171,6 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D @Override public synchronized void onClick(View view) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(eventTime); switch (view.getId()) { case R.id.ok: submit(); @@ -177,28 +178,6 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D case R.id.cancel: dismiss(); break; - case R.id.newcarbs_eventdate: - DatePickerDialog dpd = DatePickerDialog.newInstance( - this, - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH) - ); - dpd.setThemeDark(true); - dpd.dismissOnPause(true); - dpd.show(getActivity().getFragmentManager(), "Datepickerdialog"); - break; - case R.id.newcarb_eventtime: - TimePickerDialog tpd = TimePickerDialog.newInstance( - this, - calendar.get(Calendar.HOUR_OF_DAY), - calendar.get(Calendar.MINUTE), - DateFormat.is24HourFormat(getActivity()) - ); - tpd.setThemeDark(true); - tpd.dismissOnPause(true); - tpd.show(getActivity().getFragmentManager(), "Timepickerdialog"); - break; case R.id.newcarbs_plus1: editCarbs.setValue(Math.max(0, editCarbs.getValue() + SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))); @@ -304,39 +283,35 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D } okClicked = true; try { - final Integer carbs = SafeParse.stringToInt(editCarbs.getText()); + final Profile currentProfile = MainApp.getConfigBuilder().getProfile(); + if (currentProfile == null) { + return; + } + + int carbs = editCarbs.getValue().intValue(); Integer carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(new Constraint<>(carbs)).value(); + final String units = currentProfile.getUnits(); + DefaultValueHelper helper = new DefaultValueHelper(); + + int activityTTDuration = helper.determineActivityTTDuration(); + double activityTT = helper.determineActivityTT(units); + + int eatingSoonTTDuration = helper.determineEatingSoonTTDuration(); + double eatingSoonTT = helper.determineEatingSoonTT(units); + + int hypoTTDuration = helper.determineHypoTTDuration(); + double hypoTT = helper.determineHypoTT(units); + List actions = new LinkedList<>(); - if (carbs > 0) - actions.add(MainApp.gs(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""); - if (!carbsAfterConstraints.equals(carbs)) - actions.add("" + MainApp.gs(R.string.carbsconstraintapplied) + ""); - - final Profile currentProfile = MainApp.getConfigBuilder().getProfile(); - if (currentProfile == null) - return; - - int activityTTDuration = SP.getInt(R.string.key_activity_duration, Constants.defaultActivityTTDuration); - activityTTDuration = activityTTDuration > 0 ? activityTTDuration : Constants.defaultActivityTTDuration; - double activityTT = SP.getDouble(R.string.key_activity_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultActivityTTmmol : Constants.defaultActivityTTmgdl); - activityTT = activityTT > 0 ? activityTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultActivityTTmmol : Constants.defaultActivityTTmgdl; - - int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); - eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration; - double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl); - eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl; - - int hypoTTDuration = SP.getInt(R.string.key_hypo_duration, Constants.defaultHypoTTDuration); - hypoTTDuration = hypoTTDuration > 0 ? hypoTTDuration : Constants.defaultHypoTTDuration; - double hypoTT = SP.getDouble(R.string.key_hypo_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultHypoTTmmol : Constants.defaultHypoTTmgdl); - hypoTT = hypoTT > 0 ? hypoTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultHypoTTmmol : Constants.defaultHypoTTmgdl; if (startActivityTTCheckbox.isChecked()) { + String unitLabel = "mg/dl"; if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " mmol/l (" + activityTTDuration + " min)"); - } else - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(activityTT) + " mg/dl (" + activityTTDuration + " min)"); + unitLabel = "mmol/l"; + } + + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " min)"); } if (startEatingSoonTTCheckbox.isChecked()) { @@ -352,6 +327,26 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(hypoTT) + " mg/dl (" + hypoTTDuration + " min)"); } + int timeOffset = editTime.getValue().intValue(); + final long time = now() + timeOffset * 1000 * 60; + if (timeOffset != 0) { + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)); + } + int duration = editDuration.getValue().intValue(); + if (duration > 0) { + actions.add(MainApp.gs(R.string.duration) + ": " + duration + MainApp.gs(R.string.shorthour)); + } + if (carbs > 0) { + actions.add(MainApp.gs(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""); + } + if (!carbsAfterConstraints.equals(carbs)) { + actions.add("" + MainApp.gs(R.string.carbsconstraintapplied) + ""); + } + final String notes = notesEdit.getText().toString(); + if (!notes.isEmpty()) { + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); + } + final double finalActivityTT = activityTT; final int finalActivityTTDuration = activityTTDuration; final double finalEatigSoonTT = eatingSoonTT; @@ -359,26 +354,18 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D final double finalHypoTT = hypoTT; final int finalHypoTTDuration = hypoTTDuration; - if (!initialEventTime.equals(eventTime)) { - actions.add("Time: " + DateUtil.dateAndTimeString(eventTime)); - } - - final int finalCarbsAfterConstraints = carbsAfterConstraints; - - final Context context = getContext(); - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(actions.isEmpty() - ? MainApp.gs(R.string.no_action_selected) - : Html.fromHtml(Joiner.on("
").join(actions))); - builder.setPositiveButton(MainApp.gs(R.string.ok), actions.isEmpty() ? null : (dialog, id) -> { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; + if (carbsAfterConstraints > 0 || startActivityTTCheckbox.isChecked() + || startEatingSoonTTCheckbox.isChecked() || startHypoTTCheckbox.isChecked()) { + builder.setMessage(Html.fromHtml(Joiner.on("
").join(actions))); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; if (startActivityTTCheckbox.isChecked()) { TempTarget tempTarget = new TempTarget() @@ -388,7 +375,7 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D .source(Source.USER) .low(Profile.toMgdl(finalActivityTT, currentProfile.getUnits())) .high(Profile.toMgdl(finalActivityTT, currentProfile.getUnits())); - MainApp.getDbHelper().createOrUpdate(tempTarget); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } else if (startEatingSoonTTCheckbox.isChecked()) { TempTarget tempTarget = new TempTarget() .date(System.currentTimeMillis()) @@ -397,7 +384,7 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D .source(Source.USER) .low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())) .high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())); - MainApp.getDbHelper().createOrUpdate(tempTarget); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } else if (startHypoTTCheckbox.isChecked()) { TempTarget tempTarget = new TempTarget() .date(System.currentTimeMillis()) @@ -406,36 +393,28 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D .source(Source.USER) .low(Profile.toMgdl(finalHypoTT, currentProfile.getUnits())) .high(Profile.toMgdl(finalHypoTT, currentProfile.getUnits())); - MainApp.getDbHelper().createOrUpdate(tempTarget); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } - if (finalCarbsAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.date = eventTime.getTime(); - detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION; - detailedBolusInfo.carbs = finalCarbsAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - if (ConfigBuilderPlugin.getActivePump().getPumpDescription().storesCarbInfo) { - ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } + if (carbsAfterConstraints > 0) { + if (duration == 0) { + createCarb(carbsAfterConstraints, time, notes); + } else { + long remainingCarbs = carbsAfterConstraints; + int ticks = (duration * 4); //duration guaranteed to be integer greater zero + for (int i = 0; i < ticks; i++){ + long carbTime = time + i * 15 * 60 * 1000; + long smallCarbAmount = Math.round((1d * remainingCarbs) / (ticks-i)); //on last iteration (ticks-i) is 1 -> smallCarbAmount == remainingCarbs + remainingCarbs -= smallCarbAmount; + createCarb(smallCarbAmount, carbTime, notes); } - }); - } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + } } } - } - }); + }); + } else { + builder.setMessage(MainApp.gs(R.string.no_action_selected)); + } builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); dismiss(); @@ -444,19 +423,30 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, D } } - @Override - public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) { - eventTime.setYear(year - 1900); - eventTime.setMonth(monthOfYear); - eventTime.setDate(dayOfMonth); - dateButton.setText(DateUtil.dateString(eventTime)); - } - - @Override - public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute, int second) { - eventTime.setHours(hourOfDay); - eventTime.setMinutes(minute); - eventTime.setSeconds(second); - timeButton.setText(DateUtil.timeString(eventTime)); + private void createCarb(long carbs, long time, @Nullable String notes) { + DetailedBolusInfo carbInfo = new DetailedBolusInfo(); + carbInfo.date = time; + carbInfo.eventType = CareportalEvent.CARBCORRECTION; + carbInfo.carbs = carbs; + carbInfo.context = getContext(); + carbInfo.source = Source.USER; + carbInfo.notes = notes; + if (ConfigBuilderPlugin.getActivePump().getPumpDescription().storesCarbInfo) { + ConfigBuilderPlugin.getCommandQueue().bolus(carbInfo, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + }); + } else { + TreatmentsPlugin.getPlugin().addToHistoryTreatment(carbInfo); + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java index ce19009cf8..3539bee093 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java @@ -9,7 +9,6 @@ import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.Html; import android.text.TextWatcher; -import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -18,19 +17,16 @@ import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.CheckBox; -import android.widget.TextView; +import android.widget.EditText; +import android.widget.LinearLayout; import com.crashlytics.android.answers.CustomEvent; import com.google.common.base.Joiner; -import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; -import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout; -import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Calendar; -import java.util.Date; +import java.text.DecimalFormat; import java.util.LinkedList; import java.util.List; @@ -54,30 +50,25 @@ import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; import info.nightscout.utils.ToastUtils; -public class NewInsulinDialog extends DialogFragment implements OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener { +import static info.nightscout.utils.DateUtil.now; + +public class NewInsulinDialog extends DialogFragment implements OnClickListener { private static Logger log = LoggerFactory.getLogger(NewInsulinDialog.class); - private NumberPicker editInsulin; - - private TextView dateButton; - private TextView timeButton; - - private Date initialEventTime; - private Date eventTime; - - private Button plus1Button; - private Button plus2Button; - private Button plus3Button; - public static final double PLUS1_DEFAULT = 0.5d; public static final double PLUS2_DEFAULT = 1d; public static final double PLUS3_DEFAULT = 2d; - private CheckBox startESMCheckbox; + private CheckBox startEatingSoonTTCheckbox; private CheckBox recordOnlyCheckbox; + private LinearLayout editLayout; + private NumberPicker editTime; + private NumberPicker editInsulin; private Double maxInsulin; + private EditText notesEdit; + //one shot guards private boolean accepted; private boolean okClicked; @@ -90,6 +81,7 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, final private TextWatcher textWatcher = new TextWatcher() { @Override public void afterTextChanged(Editable s) { + validateInputs(); } @Override @@ -98,12 +90,16 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - validateInputs(); } }; private void validateInputs() { - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); + int time = editTime.getValue().intValue(); + if (Math.abs(time) > 12 * 60) { + editTime.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); + } + Double insulin = editInsulin.getValue(); if (insulin > maxInsulin) { editInsulin.setValue(0d); ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.bolusconstraintapplied)); @@ -121,39 +117,34 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + startEatingSoonTTCheckbox = view.findViewById(R.id.newinsulin_start_eating_soon_tt); + + recordOnlyCheckbox = view.findViewById(R.id.newinsulin_record_only); + recordOnlyCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> editLayout.setVisibility(isChecked ? View.VISIBLE : View.GONE)); + + editLayout = view.findViewById(R.id.newinsulin_time_layout); + editLayout.setVisibility(View.GONE); + editTime = view.findViewById(R.id.newinsulin_time); + editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, textWatcher); + maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value(); - editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newinsulin_amount); - + editInsulin = view.findViewById(R.id.newinsulin_amount); editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher); - dateButton = (TextView) view.findViewById(R.id.newinsulin_eventdate); - timeButton = (TextView) view.findViewById(R.id.newinsulin_eventtime); - - initialEventTime = new Date(); - eventTime = new Date(initialEventTime.getTime()); - dateButton.setText(DateUtil.dateString(eventTime)); - timeButton.setText(DateUtil.timeString(eventTime)); - dateButton.setOnClickListener(this); - timeButton.setOnClickListener(this); - - plus1Button = (Button) view.findViewById(R.id.newinsulin_plus05); + Button plus1Button = view.findViewById(R.id.newinsulin_plus05); plus1Button.setOnClickListener(this); plus1Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))); - plus2Button = (Button) view.findViewById(R.id.newinsulin_plus10); + Button plus2Button = view.findViewById(R.id.newinsulin_plus10); plus2Button.setOnClickListener(this); plus2Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))); - plus3Button = (Button) view.findViewById(R.id.newinsulin_plus20); + Button plus3Button = view.findViewById(R.id.newinsulin_plus20); plus3Button.setOnClickListener(this); plus3Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))); - startESMCheckbox = (CheckBox) view.findViewById(R.id.newinsulin_start_eating_soon_tt); - - recordOnlyCheckbox = (CheckBox) view.findViewById(R.id.newinsulin_record_only); - recordOnlyCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (dateButton != null) dateButton.setEnabled(isChecked); - if (timeButton != null) timeButton.setEnabled(isChecked); - }); + LinearLayout notesLayout = view.findViewById(R.id.newinsulin_notes_layout); + notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); + notesEdit = view.findViewById(R.id.newinsulin_notes); setCancelable(true); getDialog().setCanceledOnTouchOutside(false); @@ -167,8 +158,6 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, @Override public synchronized void onClick(View view) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(eventTime); switch (view.getId()) { case R.id.ok: submit(); @@ -176,28 +165,6 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, case R.id.cancel: dismiss(); break; - case R.id.newinsulin_eventdate: - DatePickerDialog dpd = DatePickerDialog.newInstance( - this, - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH) - ); - dpd.setThemeDark(true); - dpd.dismissOnPause(true); - dpd.show(getActivity().getFragmentManager(), "Datepickerdialog"); - break; - case R.id.newinsulin_eventtime: - TimePickerDialog tpd = TimePickerDialog.newInstance( - this, - calendar.get(Calendar.HOUR_OF_DAY), - calendar.get(Calendar.MINUTE), - DateFormat.is24HourFormat(getActivity()) - ); - tpd.setThemeDark(true); - tpd.dismissOnPause(true); - tpd.show(getActivity().getFragmentManager(), "Timepickerdialog"); - break; case R.id.newinsulin_plus05: editInsulin.setValue(Math.max(0, editInsulin.getValue() + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))); @@ -225,6 +192,10 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, okClicked = true; try { + Profile currentProfile = MainApp.getConfigBuilder().getProfile(); + if (currentProfile == null) + return; + Double insulin = SafeParse.stringToDouble(editInsulin.getText()); Double insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(insulin)).value(); @@ -239,92 +210,90 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, if (!insulinAfterConstraints.equals(insulin)) actions.add("" + MainApp.gs(R.string.bolusconstraintapplied) + ""); - double prefTTDuration = SP.getDouble(R.string.key_eatingsoon_duration, 45d); - double ttDuration = prefTTDuration > 0 ? prefTTDuration : 45d; - double prefTT = SP.getDouble(R.string.key_eatingsoon_target, 80d); - Profile currentProfile = MainApp.getConfigBuilder().getProfile(); - if (currentProfile == null) - return; - double tt; - if (currentProfile.getUnits().equals(Constants.MMOL)) - tt = prefTT > 0 ? Profile.toMgdl(prefTT, Constants.MMOL) : 80d; - else - tt = prefTT > 0 ? prefTT : 80d; - final double finalTT = tt; + int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); + eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration; + double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl); + eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl; - if (startESMCheckbox.isChecked()) { - if (currentProfile.getUnits().equals("mmol")) { - actions.add("TT: " + "" + Profile.toMmol(tt, Constants.MGDL) + " mmol for " + ((int) ttDuration) + " min "); + if (startEatingSoonTTCheckbox.isChecked()) { + if (currentProfile.getUnits().equals(Constants.MMOL)) { + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); } else - actions.add("TT: " + "" + ((int) tt) + "mg/dl for " + ((int) ttDuration) + " min "); + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); } - if (!initialEventTime.equals(eventTime)) { - actions.add("Time: " + DateUtil.dateAndTimeString(eventTime)); + int timeOffset = editTime.getValue().intValue(); + final long time = now() + timeOffset * 1000 * 60; + if (timeOffset != 0) { + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)); + } + final String notes = notesEdit.getText().toString(); + if (!notes.isEmpty()) { + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); } final double finalInsulinAfterConstraints = insulinAfterConstraints; + final double finalEatigSoonTT = eatingSoonTT; + final int finalEatingSoonTTDuration = eatingSoonTTDuration; final Context context = getContext(); final AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(actions.isEmpty() - ? MainApp.gs(R.string.no_action_selected) - : Html.fromHtml(Joiner.on("
").join(actions))); - builder.setPositiveButton(MainApp.gs(R.string.ok), actions.isEmpty() ? null : (dialog, id) -> { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; + if (finalInsulinAfterConstraints > 0 || startEatingSoonTTCheckbox.isChecked()) { + builder.setMessage(Html.fromHtml(Joiner.on("
").join(actions))); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; - if (startESMCheckbox.isChecked()) { - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration((int) ttDuration) - .reason("Eating soon") - .source(Source.USER) - .low((int) finalTT) - .high((int) finalTT); - MainApp.getDbHelper().createOrUpdate(tempTarget); - } + if (startEatingSoonTTCheckbox.isChecked()) { + TempTarget tempTarget = new TempTarget() + .date(System.currentTimeMillis()) + .duration(finalEatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())) + .high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + } - if (finalInsulinAfterConstraints <= 0.01) { - return; - } - - if (recordOnlyCheckbox.isChecked()) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.date = eventTime.getTime(); - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); - } else { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } + if (finalInsulinAfterConstraints > 0) { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; + detailedBolusInfo.insulin = finalInsulinAfterConstraints; + detailedBolusInfo.context = context; + detailedBolusInfo.source = Source.USER; + detailedBolusInfo.notes = notes; + if (recordOnlyCheckbox.isChecked()) { + detailedBolusInfo.date = time; + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + } else { + detailedBolusInfo.date = now(); + ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("Bolus")); } - }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("Bolus")); + } } - } - }); + }); + } else { + builder.setMessage(MainApp.gs(R.string.no_action_selected)); + } builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); dismiss(); @@ -332,20 +301,4 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener, log.error("Unhandled exception", e); } } - - @Override - public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) { - eventTime.setYear(year - 1900); - eventTime.setMonth(monthOfYear); - eventTime.setDate(dayOfMonth); - dateButton.setText(DateUtil.dateString(eventTime)); - } - - @Override - public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute, int second) { - eventTime.setHours(hourOfDay); - eventTime.setMinutes(minute); - eventTime.setSeconds(second); - timeButton.setText(DateUtil.timeString(eventTime)); - } } 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 f44e570774..edee0c838d 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 @@ -21,6 +21,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; @@ -53,6 +54,7 @@ import info.nightscout.androidaps.events.EventFeatureRunning; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; @@ -103,6 +105,9 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com NumberPicker editCorr; NumberPicker editCarbTime; + LinearLayout notesLayout; + EditText notesEdit; + Integer calculatedCarbs = 0; Double calculatedTotalInsulin = 0d; JSONObject boluscalcJSON; @@ -205,6 +210,10 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com superbolus = (TextView) view.findViewById(R.id.treatments_wizard_sb); superbolusInsulin = (TextView) view.findViewById(R.id.treatments_wizard_sbinsulin); + notesLayout = view.findViewById(R.id.treatments_wizard_notes_layout); + notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); + notesEdit = (EditText) view.findViewById(R.id.treatment_wizard_notes); + bgTrend = (TextView) view.findViewById(R.id.treatments_wizard_bgtrend); bgTrendInsulin = (TextView) view.findViewById(R.id.treatments_wizard_bgtrendinsulin); cobLayout = (LinearLayout) view.findViewById(R.id.treatments_wizard_cob_layout); @@ -320,6 +329,7 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com final Double bg = SafeParse.stringToDouble(editBg.getText()); final int carbTime = SafeParse.stringToInt(editCarbTime.getText()); final boolean useSuperBolus = superbolusCheckbox.isChecked(); + final String finalNotes = notesEdit.getText().toString(); final AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.sResources.getString(R.string.confirmation)); @@ -334,9 +344,9 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com accepted = true; if (finalInsulinAfterConstraints > 0 || finalCarbsAfterConstraints > 0) { if (useSuperBolus) { - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - if (activeloop != null) { - activeloop.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000); + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000); MainApp.bus().post(new EventRefreshOverview("WizardDialog")); } ConfigBuilderPlugin.getCommandQueue().tempBasalPercent(0, 120, true, profile, new Callback() { @@ -363,6 +373,7 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com detailedBolusInfo.carbTime = carbTime; detailedBolusInfo.boluscalc = boluscalcJSON; detailedBolusInfo.source = Source.USER; + detailedBolusInfo.notes = finalNotes; if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getActivePump().getPumpDescription().storesCarbInfo) { ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { @Override 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 d6c9c70c4e..b22d0450e0 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 @@ -79,6 +79,7 @@ import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.events.EventTempBasalChange; @@ -457,14 +458,14 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); if (v == apsModeView) { - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); final PumpDescription pumpDescription = ConfigBuilderPlugin.getActivePump().getPumpDescription(); - if (activeloop == null || !MainApp.getConfigBuilder().isProfileValid("ContexMenuCreation")) + if (loopPlugin == null || !MainApp.getConfigBuilder().isProfileValid("ContexMenuCreation")) return; menu.setHeaderTitle(MainApp.gs(R.string.loop)); - if (activeloop.isEnabled(PluginType.LOOP)) { + if (loopPlugin.isEnabled(PluginType.LOOP)) { menu.add(MainApp.gs(R.string.disableloop)); - if (!activeloop.isSuspended()) { + if (!loopPlugin.isSuspended()) { menu.add(MainApp.gs(R.string.suspendloopfor1h)); menu.add(MainApp.gs(R.string.suspendloopfor2h)); menu.add(MainApp.gs(R.string.suspendloopfor3h)); @@ -480,7 +481,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, menu.add(MainApp.gs(R.string.resume)); } } - if (!activeloop.isEnabled(PluginType.LOOP)) + if (!loopPlugin.isEnabled(PluginType.LOOP)) menu.add(MainApp.gs(R.string.enableloop)); } else if (v == activeProfileView) { menu.setHeaderTitle(MainApp.gs(R.string.profile)); @@ -496,13 +497,13 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, final Profile profile = MainApp.getConfigBuilder().getProfile(); if (profile == null) return true; - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); if (item.getTitle().equals(MainApp.gs(R.string.disableloop))) { - activeloop.setPluginEnabled(PluginType.LOOP, false); - activeloop.setFragmentVisible(PluginType.LOOP, false); + loopPlugin.setPluginEnabled(PluginType.LOOP, false); + loopPlugin.setFragmentVisible(PluginType.LOOP, false); MainApp.getConfigBuilder().storeSettings("DisablingLoop"); updateGUI("suspendmenu"); - MainApp.getConfigBuilder().getCommandQueue().cancelTempBasal(true, new Callback() { + ConfigBuilderPlugin.getCommandQueue().cancelTempBasal(true, new Callback() { @Override public void run() { if (!result.success) { @@ -513,16 +514,16 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, NSUpload.uploadOpenAPSOffline(24 * 60); // upload 24h, we don't know real duration return true; } else if (item.getTitle().equals(MainApp.gs(R.string.enableloop))) { - activeloop.setPluginEnabled(PluginType.LOOP, true); - activeloop.setFragmentVisible(PluginType.LOOP, true); + loopPlugin.setPluginEnabled(PluginType.LOOP, true); + loopPlugin.setFragmentVisible(PluginType.LOOP, true); MainApp.getConfigBuilder().storeSettings("EnablingLoop"); updateGUI("suspendmenu"); NSUpload.uploadOpenAPSOffline(0); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.resume))) { - activeloop.suspendTo(0L); + loopPlugin.suspendTo(0L); updateGUI("suspendmenu"); - MainApp.getConfigBuilder().getCommandQueue().cancelTempBasal(true, new Callback() { + ConfigBuilderPlugin.getCommandQueue().cancelTempBasal(true, new Callback() { @Override public void run() { if (!result.success) { @@ -673,36 +674,34 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, private void onClickAcceptTemp() { Profile profile = MainApp.getConfigBuilder().getProfile(); - if (ConfigBuilderPlugin.getActiveLoop() != null && profile != null) { - ConfigBuilderPlugin.getActiveLoop().invoke("Accept temp button", false); + if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && profile != null) { + LoopPlugin.getPlugin().invoke("Accept temp button", false); final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; if (finalLastRun != null && finalLastRun.lastAPSRun != null && finalLastRun.constraintsProcessed.isChangeRequested()) { 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) { - hideTempRecommendation(); - clearNotification(); - MainApp.getConfigBuilder().applyTBRRequest(finalLastRun.constraintsProcessed, profile, new Callback() { - @Override - public void run() { - if (result.enacted) { - finalLastRun.tbrSetByPump = result; - finalLastRun.lastEnact = new Date(); - finalLastRun.lastOpenModeAccept = new Date(); - NSUpload.uploadDeviceStatus(); - ObjectivesPlugin objectivesPlugin = MainApp.getSpecificPlugin(ObjectivesPlugin.class); - if (objectivesPlugin != null) { - ObjectivesPlugin.manualEnacts++; - ObjectivesPlugin.saveProgress(); - } + builder.setPositiveButton(getContext().getString(R.string.ok), (dialog, id) -> { + hideTempRecommendation(); + clearNotification(); + MainApp.getConfigBuilder().applyTBRRequest(finalLastRun.constraintsProcessed, profile, new Callback() { + @Override + public void run() { + if (result.enacted) { + finalLastRun.tbrSetByPump = result; + finalLastRun.lastEnact = new Date(); + finalLastRun.lastOpenModeAccept = new Date(); + NSUpload.uploadDeviceStatus(); + ObjectivesPlugin objectivesPlugin = MainApp.getSpecificPlugin(ObjectivesPlugin.class); + if (objectivesPlugin != null) { + ObjectivesPlugin.manualEnacts++; + ObjectivesPlugin.saveProgress(); } - scheduleUpdateGUI("onClickAcceptTemp"); } - }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("AcceptTemp")); - } + scheduleUpdateGUI("onClickAcceptTemp"); + } + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("AcceptTemp")); }); builder.setNegativeButton(getContext().getString(R.string.cancel), null); builder.show(); @@ -768,57 +767,55 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, accepted = false; builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(confirmMessage); - builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; - if (finalInsulinAfterConstraints > 0 || finalCarbsAfterConstraints > 0) { - if (wizard.superBolus) { - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - if (activeloop != null) { - activeloop.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000); - MainApp.bus().post(new EventRefreshOverview("WizardDialog")); - } - ConfigBuilderPlugin.getCommandQueue().tempBasalPercent(0, 120, true, profile, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }); + builder.setPositiveButton(getString(R.string.ok), (dialog, id) -> { + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; + if (finalInsulinAfterConstraints > 0 || finalCarbsAfterConstraints > 0) { + if (wizard.superBolus) { + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000); + MainApp.bus().post(new EventRefreshOverview("WizardDialog")); } - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD; - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.carbs = finalCarbsAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.boluscalc = boluscalcJSON; - detailedBolusInfo.source = Source.USER; - ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { + ConfigBuilderPlugin.getCommandQueue().tempBasalPercent(0, 120, true, profile, new Callback() { @Override public void run() { if (!result.success) { Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); i.putExtra("soundid", R.raw.boluserror); i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); MainApp.instance().startActivity(i); } } }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("QuickWizard")); } + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD; + detailedBolusInfo.insulin = finalInsulinAfterConstraints; + detailedBolusInfo.carbs = finalCarbsAfterConstraints; + detailedBolusInfo.context = context; + detailedBolusInfo.boluscalc = boluscalcJSON; + detailedBolusInfo.source = Source.USER; + ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("QuickWizard")); } } }); @@ -902,6 +899,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, scheduleUpdateGUI("EventTempTargetChange"); } + @Subscribe + public void onStatusEvent(final EventProfileSwitchChange ev) { + scheduleUpdateGUI("EventProfileSwitchChange"); + } + @Subscribe public void onStatusEvent(final EventPumpStatusChanged s) { Activity activity = getActivity(); @@ -1024,24 +1026,24 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, apsModeView.setVisibility(View.VISIBLE); apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.loopenabled)); apsModeView.setTextColor(Color.BLACK); - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isSuperBolus()) { + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if (loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuperBolus()) { apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended)); - apsModeView.setText(String.format(MainApp.gs(R.string.loopsuperbolusfor), activeloop.minutesToEndOfSuspend())); + apsModeView.setText(String.format(MainApp.gs(R.string.loopsuperbolusfor), loopPlugin.minutesToEndOfSuspend())); apsModeView.setTextColor(Color.WHITE); - } else if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isDisconnected()) { + } else if (loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isDisconnected()) { apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended)); - apsModeView.setText(String.format(MainApp.gs(R.string.loopdisconnectedfor), activeloop.minutesToEndOfSuspend())); + apsModeView.setText(String.format(MainApp.gs(R.string.loopdisconnectedfor), loopPlugin.minutesToEndOfSuspend())); apsModeView.setTextColor(Color.WHITE); - } else if (activeloop != null && activeloop.isEnabled(activeloop.getType()) && activeloop.isSuspended()) { + } else if (loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuspended()) { apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended)); - apsModeView.setText(String.format(MainApp.gs(R.string.loopsuspendedfor), activeloop.minutesToEndOfSuspend())); + apsModeView.setText(String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend())); apsModeView.setTextColor(Color.WHITE); } else if (pump.isSuspended()) { apsModeView.setBackgroundColor(MainApp.sResources.getColor(R.color.looppumpsuspended)); apsModeView.setText(MainApp.gs(R.string.pumpsuspended)); apsModeView.setTextColor(Color.WHITE); - } else if (activeloop != null && activeloop.isEnabled(activeloop.getType())) { + } else if (loopPlugin.isEnabled(PluginType.LOOP)) { if (closedLoopEnabled.value()) { apsModeView.setText(MainApp.gs(R.string.closedloop)); } else { @@ -1077,7 +1079,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, showAcceptButton = showAcceptButton && (finalLastRun.lastOpenModeAccept == null || finalLastRun.lastOpenModeAccept.getTime() < finalLastRun.lastAPSRun.getTime()); // never accepted or before last result showAcceptButton = showAcceptButton && finalLastRun.constraintsProcessed.isChangeRequested(); // change is requested - if (showAcceptButton && pump.isInitialized() && !pump.isSuspended() && ConfigBuilderPlugin.getActiveLoop() != null) { + if (showAcceptButton && pump.isInitialized() && !pump.isSuspended() && LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) { acceptTempLayout.setVisibility(View.VISIBLE); acceptTempButton.setText(getContext().getString(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index 1a8816d0b3..9921d236a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -172,7 +172,7 @@ public class GraphData { lastAbsoluteLineBasal = absoluteLineValue; lastLineBasal = baseBasalValue; lastTempBasal = tempBasalValue; - maxBasalValueFound = Math.max(maxBasalValueFound, basal); + maxBasalValueFound = Math.max(maxBasalValueFound, Math.max(tempBasalValue, baseBasalValue)); } basalLineArray.add(new ScaledDataPoint(toTime, lastLineBasal, basalScale)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java index 30e0d9b064..ce190b691b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java @@ -1,7 +1,5 @@ package info.nightscout.androidaps.plugins.ProfileLocal; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import org.json.JSONArray; @@ -70,18 +68,15 @@ public class LocalProfilePlugin extends PluginBase implements ProfileInterface { } public synchronized void storeSettings() { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); - SharedPreferences.Editor editor = settings.edit(); - editor.putBoolean(LOCAL_PROFILE + "mmol", mmol); - editor.putBoolean(LOCAL_PROFILE + "mgdl", mgdl); - editor.putString(LOCAL_PROFILE + "dia", dia.toString()); - editor.putString(LOCAL_PROFILE + "ic", ic.toString()); - editor.putString(LOCAL_PROFILE + "isf", isf.toString()); - editor.putString(LOCAL_PROFILE + "basal", basal.toString()); - editor.putString(LOCAL_PROFILE + "targetlow", targetLow.toString()); - editor.putString(LOCAL_PROFILE + "targethigh", targetHigh.toString()); + SP.putBoolean(LOCAL_PROFILE + "mmol", mmol); + SP.putBoolean(LOCAL_PROFILE + "mgdl", mgdl); + SP.putString(LOCAL_PROFILE + "dia", dia.toString()); + SP.putString(LOCAL_PROFILE + "ic", ic.toString()); + SP.putString(LOCAL_PROFILE + "isf", isf.toString()); + SP.putString(LOCAL_PROFILE + "basal", basal.toString()); + SP.putString(LOCAL_PROFILE + "targetlow", targetLow.toString()); + SP.putString(LOCAL_PROFILE + "targethigh", targetHigh.toString()); - editor.apply(); createAndStoreConvertedProfile(); edited = false; if (Config.logPrefsChange) 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 8308a2eb92..a0192a528c 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 @@ -279,8 +279,7 @@ public class SmsCommunicatorPlugin extends PluginBase { FabricPrivacy.getInstance().logCustom(new CustomEvent("SMS_Loop_Status")); break; case "RESUME": - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - activeloop.suspendTo(0); + LoopPlugin.getPlugin().suspendTo(0); MainApp.bus().post(new EventRefreshOverview("SMS_LOOP_RESUME")); NSUpload.uploadOpenAPSOffline(0); reply = MainApp.sResources.getString(R.string.smscommunicator_loopresumed); @@ -517,8 +516,7 @@ public class SmsCommunicatorPlugin extends PluginBase { @Override public void run() { if (result.success) { - final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - activeloop.suspendTo(System.currentTimeMillis() + suspendWaitingForConfirmation.duration * 60L * 1000); + LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + suspendWaitingForConfirmation.duration * 60L * 1000); NSUpload.uploadOpenAPSOffline(suspendWaitingForConfirmation.duration * 60); MainApp.bus().post(new EventRefreshOverview("SMS_LOOP_SUSPENDED")); String reply = MainApp.sResources.getString(R.string.smscommunicator_loopsuspended) + " " + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java index 0d33f529b3..f47d25abb1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java @@ -12,6 +12,8 @@ import info.nightscout.androidaps.interfaces.PluginType; public class SourceXdripPlugin extends PluginBase implements BgSourceInterface { private static SourceXdripPlugin plugin = null; + + boolean advancedFiltering; public static SourceXdripPlugin getPlugin() { if (plugin == null) @@ -29,6 +31,10 @@ public class SourceXdripPlugin extends PluginBase implements BgSourceInterface { @Override public boolean advancedFilteringSupported() { - return false; + return advancedFiltering; + } + + public void setSource(String source) { + this.advancedFiltering = "G5 Native".equals(source); } } 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 36fac67834..8f82f71c35 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 @@ -452,7 +452,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface //log.debug("Adding new Treatment record" + carbsTreatment); } if (newRecordCreated && detailedBolusInfo.isValid) - NSUpload.uploadBolusWizardRecord(detailedBolusInfo); + NSUpload.uploadTreatmentRecord(detailedBolusInfo); return newRecordCreated; } @@ -505,6 +505,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override + public void addToHistoryTempTarget(TempTarget tempTarget) { + //log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); + MainApp.getDbHelper().createOrUpdate(tempTarget); + NSUpload.uploadTempTarget(tempTarget); + } + // Profile Switch @Subscribe @SuppressWarnings("unused") diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java index 87626ed4cd..ae977d4024 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java @@ -89,6 +89,10 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. holder.iob.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); else holder.iob.setTextColor(holder.carbs.getCurrentTextColor()); + if (t.date > DateUtil.now()) + holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorScheduled)); + else + holder.date.setTextColor(holder.carbs.getCurrentTextColor()); holder.remove.setTag(t); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java index d018efd97b..5ce79636b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java @@ -84,14 +84,10 @@ public class TreatmentsTempTargetFragment extends SubscriberFragment implements holder.reasonLabel.setText(""); holder.reasonColon.setText(""); } - if (tempTarget.isInProgress()) { - if (tempTarget == currentlyActiveTarget) { - // active as newest - holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorInProgress)); - } else { - // other's that might become active again after the latest (overlapping) is over - holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); - } + if (tempTarget.isInProgress() && tempTarget == currentlyActiveTarget) { + holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); + } else if (tempTarget.date > DateUtil.now()) { + holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorScheduled)); } else { holder.date.setTextColor(holder.reasonColon.getCurrentTextColor()); } 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 48969d3dce..fdc14f32d4 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 @@ -441,21 +441,21 @@ public class ActionStringHandler { private static String getLoopStatus() { String ret = ""; // decide if enabled/disabled closed/open; what Plugin as APS? - final LoopPlugin activeloop = MainApp.getConfigBuilder().getActiveLoop(); - if (activeloop != null && activeloop.isEnabled(activeloop.getType())) { + final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if (loopPlugin.isEnabled(loopPlugin.getType())) { if (MainApp.getConstraintChecker().isClosedLoopAllowed().value()) { ret += "CLOSED LOOP\n"; } else { ret += "OPEN LOOP\n"; } - final APSInterface aps = MainApp.getConfigBuilder().getActiveAPS(); + final APSInterface aps = ConfigBuilderPlugin.getActiveAPS(); ret += "APS: " + ((aps == null) ? "NO APS SELECTED!" : ((PluginBase) aps).getName()); - if (activeloop.lastRun != null) { - if (activeloop.lastRun.lastAPSRun != null) - ret += "\nLast Run: " + DateUtil.timeString(activeloop.lastRun.lastAPSRun); + if (LoopPlugin.lastRun != null) { + if (LoopPlugin.lastRun.lastAPSRun != null) + ret += "\nLast Run: " + DateUtil.timeString(LoopPlugin.lastRun.lastAPSRun); - if (activeloop.lastRun.lastEnact != null) - ret += "\nLast Enact: " + DateUtil.timeString(activeloop.lastRun.lastEnact); + if (LoopPlugin.lastRun.lastEnact != null) + ret += "\nLast Enact: " + DateUtil.timeString(LoopPlugin.lastRun.lastEnact); } @@ -502,7 +502,7 @@ public class ActionStringHandler { return "No profile set :("; } - APSInterface usedAPS = MainApp.getConfigBuilder().getActiveAPS(); + APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS(); if (usedAPS == null) { return "No active APS :(!"; } @@ -622,10 +622,7 @@ public class ActionStringHandler { } else { tempTarget.low(0).high(0); } - MainApp.getDbHelper().createOrUpdate(tempTarget); - - //TODO: Nightscout-Treatment for Temp-Target! - //ConfigBuilderPlugin.uploadCareportalEntryToNS(data); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } private static void doFillBolus(final Double amount) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java index 668ec3f829..dcda0bc0aa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java @@ -140,11 +140,7 @@ public class WearPlugin extends PluginBase { @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { - - LoopPlugin activeloop = MainApp.getConfigBuilder().getActiveLoop(); - if (activeloop == null) return; - - if (WatchUpdaterService.shouldReportLoopStatus(activeloop.isEnabled(PluginType.LOOP))) { + if (WatchUpdaterService.shouldReportLoopStatus(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) { sendDataToWatch(true, false, false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java index bde197b45f..a92738d336 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java @@ -666,12 +666,12 @@ public class WatchUpdaterService extends WearableListenerService implements return status; } - LoopPlugin activeloop = MainApp.getConfigBuilder().getActiveLoop(); + LoopPlugin activeloop = LoopPlugin.getPlugin(); - if (activeloop != null && !activeloop.isEnabled(PluginType.LOOP)) { + if (!activeloop.isEnabled(PluginType.LOOP)) { status += getString(R.string.disabledloop) + "\n"; lastLoopStatus = false; - } else if (activeloop != null && activeloop.isEnabled(PluginType.LOOP)) { + } else { lastLoopStatus = true; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java index b6b3c07e34..5ce277e399 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java @@ -108,12 +108,12 @@ public class StatuslinePlugin extends PluginBase { @NonNull private String buildStatusString(Profile profile) { String status = ""; - LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); + LoopPlugin loopPlugin = LoopPlugin.getPlugin(); - if (activeloop != null && !activeloop.isEnabled(PluginType.LOOP)) { + if (!loopPlugin.isEnabled(PluginType.LOOP)) { status += ctx.getString(R.string.disabledloop) + "\n"; lastLoopStatus = false; - } else if (activeloop != null && activeloop.isEnabled(PluginType.LOOP)) { + } else if (loopPlugin.isEnabled(PluginType.LOOP)) { lastLoopStatus = true; } @@ -179,13 +179,8 @@ public class StatuslinePlugin extends PluginBase { @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { - //Filter events where loop is (de)activated - - LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); - if (activeloop == null) return; - - if ((lastLoopStatus != activeloop.isEnabled(PluginType.LOOP))) { + if ((lastLoopStatus != LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) { sendStatus(); } } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index fa15ee46d2..a514a85815 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -166,14 +166,19 @@ public class CommandQueue { public boolean bolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { Command.CommandType type = detailedBolusInfo.isSMB ? Command.CommandType.SMB_BOLUS : Command.CommandType.BOLUS; - if (isRunning(type)) { - if (callback != null) - callback.result(executingNowError()).run(); - return false; - } + if(type.equals(Command.CommandType.BOLUS) && detailedBolusInfo.carbs > 0 && detailedBolusInfo.insulin == 0){ + type = Command.CommandType.CARBS_ONLY_TREATMENT; + //Carbs only can be added in parallel as they can be "in the future". + } else { + if (isRunning(type)) { + if (callback != null) + callback.result(executingNowError()).run(); + return false; + } - // remove all unfinished boluses - removeAll(type); + // remove all unfinished boluses + removeAll(type); + } // apply constraints detailedBolusInfo.insulin = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(detailedBolusInfo.insulin)).value(); @@ -183,12 +188,14 @@ public class CommandQueue { if (detailedBolusInfo.isSMB) { add(new CommandSMBBolus(detailedBolusInfo, callback)); } else { - add(new CommandBolus(detailedBolusInfo, callback)); - // Bring up bolus progress dialog (start here, so the dialog is shown when the bolus is requested, - // not when the Bolus command is starting. The command closes the dialog upon completion). - showBolusProgressDialog(detailedBolusInfo.insulin, detailedBolusInfo.context); - // Notify Wear about upcoming bolus - MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); + add(new CommandBolus(detailedBolusInfo, callback, type)); + if(type.equals(Command.CommandType.BOLUS)) { + // Bring up bolus progress dialog (start here, so the dialog is shown when the bolus is requested, + // not when the Bolus command is starting. The command closes the dialog upon completion). + showBolusProgressDialog(detailedBolusInfo.insulin, detailedBolusInfo.context); + // Notify Wear about upcoming bolus + MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); + } } notifyAboutNewCommand(); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java index f9e0739b45..8ec61932c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java @@ -53,6 +53,12 @@ public class QueueThread extends Thread { try { while (true) { PumpInterface pump = ConfigBuilderPlugin.getActivePump(); + if (pump == null) { + log.debug("QUEUE: pump == null"); + MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.pumpNotInitialized))); + SystemClock.sleep(1000); + continue; + } long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000; if (!pump.isConnected() && secondsElapsed > Constants.PUMP_MAX_CONNECTION_TIME_IN_SECONDS) { diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java index c5b3ada417..b865fac86e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java @@ -12,6 +12,7 @@ public abstract class Command { public enum CommandType { BOLUS, SMB_BOLUS, + CARBS_ONLY_TREATMENT, TEMPBASAL, EXTENDEDBOLUS, BASALPROFILE, diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java index 278fd7681b..cab7d23311 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java @@ -16,8 +16,8 @@ import info.nightscout.utils.DecimalFormatter; public class CommandBolus extends Command { DetailedBolusInfo detailedBolusInfo; - public CommandBolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { - commandType = CommandType.BOLUS; + public CommandBolus(DetailedBolusInfo detailedBolusInfo, Callback callback, CommandType type) { + commandType = type; this.detailedBolusInfo = detailedBolusInfo; this.callback = callback; } diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWDefinition.java new file mode 100644 index 0000000000..e12a987bd1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWDefinition.java @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.startupwizard; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; +import info.nightscout.utils.SP; + +public class SWDefinition { + + private static SWDefinition swDefinition = null; + + public static SWDefinition getInstance() { + if (swDefinition == null) + swDefinition = new SWDefinition(); + return swDefinition; + } + + static List screens = new ArrayList<>(); + + public static List getScreens() { + return screens; + } + + SWDefinition add(SWScreen newScreen) { + screens.add(newScreen); + return this; + } + + SWDefinition() { + add(new SWScreen(R.string.nsclientinternal_title) + .skippable(false) + .add(new SWUrl().preferenceId(R.string.key_nsclientinternal_url).label(R.string.nsclientinternal_url_title).comment(R.string.nsclientinternal_url_dialogmessage)) + .add(new SWString().preferenceId(R.string.key_nsclientinternal_api_secret).label(R.string.nsclientinternal_secret_dialogtitle).comment(R.string.nsclientinternal_secret_dialogmessage)) + .validator(() -> NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth) + ) + .add(new SWScreen(R.string.patientage) + .skippable(false) + .add(new SWRadioButton().option(R.array.ageArray, R.array.ageValues).preferenceId(R.string.key_age).label(R.string.patientage).comment(R.string.patientage_summary)) + .validator(() -> SP.contains(R.string.key_age)) + ) + ; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWItem.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWItem.java new file mode 100644 index 0000000000..4e41b4c0c7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWItem.java @@ -0,0 +1,62 @@ +package info.nightscout.androidaps.startupwizard; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.utils.SP; + +public class SWItem { + enum Type { + NONE, + URL, + STRING, + NUMBER, + DECIMALNUMBER, + CHECKBOX, + RADIOBUTTON + } + + Type type; + Integer label; + Integer comment; + int preferenceId; + + + public SWItem(Type type) { + this.type = type; + } + + String getLabel() { + return MainApp.gs(label); + } + + String getComment() { + if (comment != null) + return MainApp.gs(comment); + else + return ""; + } + + Type getType() { + return type; + } + + public SWItem label(int label) { + this.label = label; + return this; + } + + public SWItem comment(int comment) { + this.comment = comment; + return this; + } + + SWItem preferenceId(int preferenceId) { + this.preferenceId = preferenceId; + return this; + } + + public void save(String value) { + SP.putString(preferenceId, value); + MainApp.bus().post(new EventPreferenceChange(preferenceId)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWRadioButton.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWRadioButton.java new file mode 100644 index 0000000000..5dc5c74438 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWRadioButton.java @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.startupwizard; + +import info.nightscout.androidaps.MainApp; + +public class SWRadioButton extends SWItem { + + int labelsArray; + int valuesArray; + + public SWRadioButton() { + super(Type.RADIOBUTTON); + } + + public SWRadioButton option(int labels, int values) { + this.labelsArray = labels; + this.valuesArray = values; + return this; + } + + public String[] labels() { + return MainApp.sResources.getStringArray(labelsArray); + } + + public String[] values() { + return MainApp.sResources.getStringArray(valuesArray); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWScreen.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWScreen.java new file mode 100644 index 0000000000..7ae674406d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWScreen.java @@ -0,0 +1,43 @@ +package info.nightscout.androidaps.startupwizard; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.MainApp; + +public class SWScreen { + + int header; + List items = new ArrayList<>(); + SWValidator validator; + boolean skippable = false; + + public SWScreen(int header) { + this.header = header; + } + + public String getHeader() { + return MainApp.gs(header); + } + + public SWScreen skippable(boolean skippable) { + this.skippable = skippable; + return this; + } + + public SWScreen add(SWItem newItem) { + items.add(newItem); + return this; + } + + public SWScreen validator(SWValidator validator) { + this.validator = validator; + return this; + } + + boolean isValid() { + if (validator != null) + return validator.isValid(); + return true; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWString.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWString.java new file mode 100644 index 0000000000..1378ca8e0f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWString.java @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.startupwizard; + +public class SWString extends SWItem { + + public SWString() { + super(Type.STRING); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWUrl.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWUrl.java new file mode 100644 index 0000000000..d9ad6076ca --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWUrl.java @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.startupwizard; + +public class SWUrl extends SWItem { + + public SWUrl() { + super(Type.URL); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SWValidator.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWValidator.java new file mode 100644 index 0000000000..e1fce21478 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SWValidator.java @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.startupwizard; + +public interface SWValidator { + boolean isValid(); +} diff --git a/app/src/main/java/info/nightscout/androidaps/startupwizard/SetupWizardActivity.java b/app/src/main/java/info/nightscout/androidaps/startupwizard/SetupWizardActivity.java new file mode 100644 index 0000000000..3fa9cd1501 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/startupwizard/SetupWizardActivity.java @@ -0,0 +1,165 @@ +package info.nightscout.androidaps.startupwizard; + +import android.annotation.SuppressLint; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.View; + +import info.nightscout.androidaps.R; + +/** + * An example full-screen activity that shows and hides the system UI (i.e. + * status bar and navigation/system bar) with user interaction. + */ +public class SetupWizardActivity extends AppCompatActivity { + /** + * Whether or not the system UI should be auto-hidden after + * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. + */ + private static final boolean AUTO_HIDE = true; + + /** + * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after + * user interaction before hiding the system UI. + */ + private static final int AUTO_HIDE_DELAY_MILLIS = 3000; + + /** + * Some older devices needs a small delay between UI widget updates + * and a change of the status and navigation bar. + */ + private static final int UI_ANIMATION_DELAY = 300; + private final Handler mHideHandler = new Handler(); + private View mContentView; + private final Runnable mHidePart2Runnable = new Runnable() { + @SuppressLint("InlinedApi") + @Override + public void run() { + // Delayed removal of status and navigation bar + + // Note that some of these constants are new as of API 16 (Jelly Bean) + // and API 19 (KitKat). It is safe to use them, as they are inlined + // at compile-time and do nothing on earlier devices. + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + }; + private View mControlsView; + private final Runnable mShowPart2Runnable = new Runnable() { + @Override + public void run() { + // Delayed display of UI elements + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.show(); + } + mControlsView.setVisibility(View.VISIBLE); + } + }; + private boolean mVisible; + private final Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hide(); + } + }; + /** + * Touch listener to use for in-layout UI controls to delay hiding the + * system UI. This is to prevent the jarring behavior of controls going away + * while interacting with activity UI. + */ + private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (AUTO_HIDE) { + delayedHide(AUTO_HIDE_DELAY_MILLIS); + } + return false; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_setupwizard); + + mVisible = true; + mControlsView = findViewById(R.id.fullscreen_content_controls); + mContentView = findViewById(R.id.fullscreen_content); + + + // Set up the user interaction to manually show or hide the system UI. + mContentView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + toggle(); + } + }); + + // Upon interacting with UI controls, delay any scheduled hide() + // operations to prevent the jarring behavior of controls going away + // while interacting with the UI. + findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // Trigger the initial hide() shortly after the activity has been + // created, to briefly hint to the user that UI controls + // are available. + delayedHide(100); + } + + private void toggle() { + if (mVisible) { + hide(); + } else { + show(); + } + } + + private void hide() { + // Hide UI first + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } + mControlsView.setVisibility(View.GONE); + mVisible = false; + + // Schedule a runnable to remove the status and navigation bar after a delay + mHideHandler.removeCallbacks(mShowPart2Runnable); + mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY); + } + + @SuppressLint("InlinedApi") + private void show() { + // Show the system bar + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + mVisible = true; + + // Schedule a runnable to display UI elements after a delay + mHideHandler.removeCallbacks(mHidePart2Runnable); + mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY); + } + + /** + * Schedules a call to hide() in delay milliseconds, canceling any + * previously scheduled calls. + */ + private void delayedHide(int delayMillis) { + mHideHandler.removeCallbacks(mHideRunnable); + mHideHandler.postDelayed(mHideRunnable, delayMillis); + } +} diff --git a/app/src/main/java/info/nightscout/utils/DefaultValueHelper.java b/app/src/main/java/info/nightscout/utils/DefaultValueHelper.java new file mode 100644 index 0000000000..ba1aba90b8 --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/DefaultValueHelper.java @@ -0,0 +1,90 @@ +package info.nightscout.utils; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.R; + +public class DefaultValueHelper { + + /** + * returns the corresponding EatingSoon TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + public double getDefaultEatingSoonTT(String units) { + return Constants.MMOL.equals(units) ? Constants.defaultEatingSoonTTmmol + : Constants.defaultEatingSoonTTmgdl; + } + + /** + * returns the corresponding Activity TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + public double getDefaultActivityTT(String units) { + return Constants.MMOL.equals(units) ? Constants.defaultActivityTTmmol + : Constants.defaultActivityTTmgdl; + } + + /** + * returns the corresponding Hypo TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + public double getDefaultHypoTT(String units) { + return Constants.MMOL.equals(units) ? Constants.defaultHypoTTmmol + : Constants.defaultHypoTTmgdl; + } + + /** + * returns the configured EatingSoon TempTarget, if this is set to 0, the Default-Value is returned. + * + * @param units + * @return + */ + public double determineEatingSoonTT(String units) { + double value = SP.getDouble(R.string.key_eatingsoon_target, this.getDefaultEatingSoonTT(units)); + return value > 0 ? value : this.getDefaultEatingSoonTT(units); + } + + public int determineEatingSoonTTDuration() { + int value = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); + return value > 0 ? value : Constants.defaultEatingSoonTTDuration; + } + + + /** + * returns the configured Activity TempTarget, if this is set to 0, the Default-Value is returned. + * + * @param units + * @return + */ + public double determineActivityTT(String units) { + double value = SP.getDouble(R.string.key_activity_target, this.getDefaultActivityTT(units)); + return value > 0 ? value : this.getDefaultActivityTT(units); + } + + public int determineActivityTTDuration() { + int value = SP.getInt(R.string.key_activity_duration, Constants.defaultActivityTTDuration); + return value > 0 ? value : Constants.defaultActivityTTDuration; + } + + /** + * returns the configured Hypo TempTarget, if this is set to 0, the Default-Value is returned. + * + * @param units + * @return + */ + public double determineHypoTT(String units) { + double value = SP.getDouble(R.string.key_hypo_target, this.getDefaultHypoTT(units)); + return value > 0 ? value : this.getDefaultHypoTT(units); + } + + public int determineHypoTTDuration() { + int value = SP.getInt(R.string.key_hypo_duration, Constants.defaultHypoTTDuration); + return value > 0 ? value : Constants.defaultHypoTTDuration; + } + +} diff --git a/app/src/main/java/info/nightscout/utils/JsonHelper.java b/app/src/main/java/info/nightscout/utils/JsonHelper.java index ead68132af..503d0bf395 100644 --- a/app/src/main/java/info/nightscout/utils/JsonHelper.java +++ b/app/src/main/java/info/nightscout/utils/JsonHelper.java @@ -1,5 +1,7 @@ package info.nightscout.utils; +import android.support.annotation.Nullable; + import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -17,6 +19,20 @@ public class JsonHelper { private JsonHelper() {}; + public static Object safeGetObject(JSONObject json, String fieldName, Object defaultValue) { + Object result = defaultValue; + + if (json.has(fieldName)) { + try { + result = json.get(fieldName); + } catch (JSONException ignored) { + } + } + + return result; + } + + @Nullable public static String safeGetString(JSONObject json, String fieldName) { String result = null; diff --git a/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java b/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java index 58814ceddb..cc1a22355a 100644 --- a/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java +++ b/app/src/main/java/info/nightscout/utils/LocalAlertUtils.java @@ -13,10 +13,10 @@ import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.Loop.LoopPlugin; +import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; -import info.nightscout.androidaps.receivers.KeepAliveReceiver; -import info.nightscout.utils.NSUpload; /** * Created by adrian on 17/12/17. @@ -25,12 +25,12 @@ import info.nightscout.utils.NSUpload; public class LocalAlertUtils { private static Logger log = LoggerFactory.getLogger(LocalAlertUtils.class); - public static int missedReadingsThreshold() { - return SP.getInt(MainApp.sResources.getString(R.string.key_missed_bg_readings_threshold), 30) * 60 * 1000; + public static long missedReadingsThreshold() { + return T.mins(SP.getInt(MainApp.sResources.getString(R.string.key_missed_bg_readings_threshold), 30)).msecs(); } - private static int pumpUnreachableThreshold() { - return SP.getInt(MainApp.sResources.getString(R.string.key_pump_unreachable_threshold), 30) * 60 * 1000; + private static long pumpUnreachableThreshold() { + return T.mins(SP.getInt(MainApp.sResources.getString(R.string.key_pump_unreachable_threshold), 30)).msecs(); } public static void checkPumpUnreachableAlarm(Date lastConnection, boolean isStatusOutdated) { @@ -38,7 +38,7 @@ public class LocalAlertUtils { boolean nextAlarmOccurrenceReached = SP.getLong("nextPumpDisconnectedAlarm", 0L) < System.currentTimeMillis(); if (Config.APS && SP.getBoolean(MainApp.sResources.getString(R.string.key_enable_pump_unreachable_alert), true) - && isStatusOutdated && alarmTimeoutExpired && nextAlarmOccurrenceReached && !ConfigBuilderPlugin.getActiveLoop().isDisconnected()) { + && isStatusOutdated && alarmTimeoutExpired && nextAlarmOccurrenceReached && !LoopPlugin.getPlugin().isDisconnected()) { log.debug("Generating pump unreachable alarm. lastConnection: " + DateUtil.dateAndTimeString(lastConnection) + " isStatusOutdated: " + isStatusOutdated); Notification n = new Notification(Notification.PUMP_UNREACHABLE, MainApp.sResources.getString(R.string.pump_unreachable), Notification.URGENT); n.soundId = R.raw.alarm; @@ -48,11 +48,13 @@ public class LocalAlertUtils { NSUpload.uploadError(n.text); } } + if (!isStatusOutdated && !alarmTimeoutExpired) + MainApp.bus().post(new EventDismissNotification(Notification.PUMP_UNREACHABLE)); } /*Presnoozes the alarms with 5 minutes if no snooze exists. - * Call only at startup! - */ + * Call only at startup! + */ public static void presnoozeAlarms() { if (SP.getLong("nextMissedReadingsAlarm", 0l) < System.currentTimeMillis()) { SP.putLong("nextMissedReadingsAlarm", System.currentTimeMillis() + 5 * 60 * 1000); @@ -73,7 +75,7 @@ public class LocalAlertUtils { SP.putLong("nextPumpDisconnectedAlarm", nextPumpDisconnectedAlarm); } - public static void notifyPumpStatusRead(){ + public static void notifyPumpStatusRead() { //TODO: persist the actual time the pump is read and simplify the whole logic when to alarm final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); diff --git a/app/src/main/java/info/nightscout/utils/NSUpload.java b/app/src/main/java/info/nightscout/utils/NSUpload.java index 9b9b940ef9..3cd252f19a 100644 --- a/app/src/main/java/info/nightscout/utils/NSUpload.java +++ b/app/src/main/java/info/nightscout/utils/NSUpload.java @@ -7,7 +7,9 @@ import android.content.pm.ResolveInfo; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.annotation.Nullable; +import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -28,6 +30,7 @@ import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; +import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Loop.APSResult; @@ -256,7 +259,7 @@ public class NSUpload { } } - public static void uploadBolusWizardRecord(DetailedBolusInfo detailedBolusInfo) { + public static void uploadTreatmentRecord(DetailedBolusInfo detailedBolusInfo) { JSONObject data = new JSONObject(); try { data.put("eventType", detailedBolusInfo.eventType); @@ -275,6 +278,9 @@ public class NSUpload { data.put("boluscalc", detailedBolusInfo.boluscalc); if (detailedBolusInfo.carbTime != 0) data.put("preBolus", detailedBolusInfo.carbTime); + if (!StringUtils.isEmpty(detailedBolusInfo.notes)) { + data.put("notes", detailedBolusInfo.notes); + } } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -302,6 +308,30 @@ public class NSUpload { } } + public static void uploadTempTarget(TempTarget tempTarget) { + try { + Profile profile = MainApp.getConfigBuilder().getProfile(); + + if (profile == null) { + log.error("Profile is null. Skipping upload"); + return; + } + + JSONObject data = new JSONObject(); + data.put("eventType", CareportalEvent.TEMPORARYTARGET); + data.put("duration", tempTarget.durationInMinutes); + data.put("reason", tempTarget.reason); + data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits())); + data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits())); + data.put("created_at", DateUtil.toISOString(tempTarget.date)); + data.put("units", profile.getUnits()); + data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + uploadCareportalEntryToNS(data); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } + public static void updateProfileSwitch(ProfileSwitch profileSwitch) { try { JSONObject data = new JSONObject(); @@ -466,7 +496,7 @@ public class NSUpload { try { data.put("eventType", "Note"); data.put("created_at", DateUtil.toISOString(new Date())); - data.put("notes", MainApp.sResources.getString(R.string.androidaps_start)); + data.put("notes", MainApp.sResources.getString(R.string.androidaps_start)+" - "+ Build.MANUFACTURER + " "+ Build.MODEL); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -476,10 +506,10 @@ public class NSUpload { intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); context.sendBroadcast(intent); DbLogger.dbAdd(intent, data.toString()); - } + } } - public static void uploadEvent(String careportalEvent, long time) { + public static void uploadEvent(String careportalEvent, long time, @Nullable String notes) { Context context = MainApp.instance().getApplicationContext(); Bundle bundle = new Bundle(); bundle.putString("action", "dbAdd"); @@ -489,6 +519,9 @@ public class NSUpload { data.put("eventType", careportalEvent); data.put("created_at", DateUtil.toISOString(time)); data.put("enteredBy", SP.getString("careportal_enteredby", MainApp.gs(R.string.app_name))); + if (notes != null) { + data.put("notes", notes); + } } catch (JSONException e) { log.error("Unhandled exception", e); } diff --git a/app/src/main/java/info/nightscout/utils/NumberPicker.java b/app/src/main/java/info/nightscout/utils/NumberPicker.java index 01c7dcb336..64239cbaee 100644 --- a/app/src/main/java/info/nightscout/utils/NumberPicker.java +++ b/app/src/main/java/info/nightscout/utils/NumberPicker.java @@ -5,6 +5,7 @@ import android.os.Handler; import android.os.Message; import android.text.Editable; import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -148,7 +149,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, } public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, TextWatcher textWatcher) { - if(this.textWatcher != null) { + if (this.textWatcher != null) { editText.removeTextChangedListener(this.textWatcher); } setParams(initValue, minValue, maxValue, step, formater, allowZero); @@ -164,6 +165,8 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, this.formater = formater; this.allowZero = allowZero; + editText.setKeyListener(DigitsKeyListener.getInstance(minValue < 0, step != Math.rint(step))); + if (textWatcher != null) editText.removeTextChangedListener(textWatcher); updateEditText(); @@ -202,7 +205,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, updateEditText(); } - private void dec( int multiplier) { + private void dec(int multiplier) { value -= step * multiplier; if (value < minValue) { value = minValue; diff --git a/app/src/main/java/info/nightscout/utils/SP.java b/app/src/main/java/info/nightscout/utils/SP.java index 16015d8956..df45996fc2 100644 --- a/app/src/main/java/info/nightscout/utils/SP.java +++ b/app/src/main/java/info/nightscout/utils/SP.java @@ -16,6 +16,10 @@ public class SP { return sharedPreferences.contains(key); } + static public boolean contains(int resourceId) { + return sharedPreferences.contains(MainApp.gs(resourceId)); + } + static public String getString(int resourceID, String defaultValue) { return sharedPreferences.getString(MainApp.sResources.getString(resourceID), defaultValue); } @@ -24,7 +28,7 @@ public class SP { return sharedPreferences.getString(key, defaultValue); } - static public boolean getBoolean(int resourceID, boolean defaultValue) { + static public boolean getBoolean(int resourceID, Boolean defaultValue) { try { return sharedPreferences.getBoolean(MainApp.sResources.getString(resourceID), defaultValue); } catch (Exception e) { @@ -32,7 +36,7 @@ public class SP { } } - static public boolean getBoolean(String key, boolean defaultValue) { + static public boolean getBoolean(String key, Boolean defaultValue) { try { return sharedPreferences.getBoolean(key, defaultValue); } catch (Exception e) { diff --git a/app/src/main/res/drawable-hdpi/icon_local_activate.png b/app/src/main/res/drawable-hdpi/icon_local_activate.png new file mode 100644 index 0000000000..4ef567ffb6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_activate.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_local_reset.png b/app/src/main/res/drawable-hdpi/icon_local_reset.png new file mode 100644 index 0000000000..7d38cf0b50 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_reset.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_local_save.png b/app/src/main/res/drawable-hdpi/icon_local_save.png new file mode 100644 index 0000000000..e5c1d5cedc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_save.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_activate.png b/app/src/main/res/drawable-mdpi/icon_local_activate.png new file mode 100644 index 0000000000..b39c72b407 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_activate.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_reset.png b/app/src/main/res/drawable-mdpi/icon_local_reset.png new file mode 100644 index 0000000000..ad2e0eaf6d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_reset.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_save.png b/app/src/main/res/drawable-mdpi/icon_local_save.png new file mode 100644 index 0000000000..843beed523 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_save.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_activate.png b/app/src/main/res/drawable-xhdpi/icon_local_activate.png new file mode 100644 index 0000000000..75e9af38ed Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_activate.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_reset.png b/app/src/main/res/drawable-xhdpi/icon_local_reset.png new file mode 100644 index 0000000000..2813cb448d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_reset.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_save.png b/app/src/main/res/drawable-xhdpi/icon_local_save.png new file mode 100644 index 0000000000..0cf81430cc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_save.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_activate.png b/app/src/main/res/drawable-xxhdpi/icon_local_activate.png new file mode 100644 index 0000000000..600e6ee61c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_activate.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_reset.png b/app/src/main/res/drawable-xxhdpi/icon_local_reset.png new file mode 100644 index 0000000000..dc9d659dd7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_reset.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_save.png b/app/src/main/res/drawable-xxhdpi/icon_local_save.png new file mode 100644 index 0000000000..20448e4bf9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_save.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_activate.png b/app/src/main/res/drawable-xxxhdpi/icon_local_activate.png new file mode 100644 index 0000000000..2de9ce9d4d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_activate.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_reset.png b/app/src/main/res/drawable-xxxhdpi/icon_local_reset.png new file mode 100644 index 0000000000..ec54479149 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_reset.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_save.png b/app/src/main/res/drawable-xxxhdpi/icon_local_save.png new file mode 100644 index 0000000000..f2ecc45ac0 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_save.png differ diff --git a/app/src/main/res/layout/actions_fill_dialog.xml b/app/src/main/res/layout/actions_fill_dialog.xml index 18a5886246..2883485bf7 100644 --- a/app/src/main/res/layout/actions_fill_dialog.xml +++ b/app/src/main/res/layout/actions_fill_dialog.xml @@ -1,97 +1,153 @@ - + android:layout_height="wrap_content"> + + + android:layout_gravity="center" + android:orientation="horizontal"> + + - + + + + + android:text="@string/careportal_pumpsitechange" /> - - - - - - - - - - - - -