diff --git a/app/build.gradle b/app/build.gradle index 51afdc54c6..75a930e03b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -158,6 +158,14 @@ android { buildConfigField "boolean", "PUMPCONTROL", "false" } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + testOptions { + unitTests.returnDefaultValues = true + } } allprojects { diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index ae3982ad68..4ea3a80d3a 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -130,6 +130,8 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResource(id); addPreferencesFromResource(R.xml.pref_advanced); } else { + addPreferencesFromResource(R.xml.pref_overview); + if (!Config.NSCLIENT && !Config.G5UPLOADER) { addPreferencesFromResource(R.xml.pref_password); } 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 new file mode 100644 index 0000000000..15e93816bd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java @@ -0,0 +1,329 @@ +package info.nightscout.androidaps.plugins.Overview.Dialogs; + +import android.content.Context; +import android.os.Bundle; +import android.os.HandlerThread; +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; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; + +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; +import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout; +import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DecimalFormat; +import java.util.Calendar; +import java.util.Date; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.androidaps.plugins.Loop.LoopPlugin; +import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalResultSMB; +import info.nightscout.androidaps.queue.Callback; +import info.nightscout.utils.DateUtil; +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 { + private static Logger log = LoggerFactory.getLogger(NewCarbsDialog.class); + + private EditText foodText; + 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 double FAV1_DEFAULT = 5; + private static final double FAV2_DEFAULT = 10; + private static final double FAV3_DEFAULT = 20; + + private CheckBox suspendLoopCheckbox; + private CheckBox startActivityTTCheckbox; + + private Integer maxCarbs; + + //one shot guards + private boolean accepted; + private boolean okClicked; + + public NewCarbsDialog() { + HandlerThread mHandlerThread = new HandlerThread(NewCarbsDialog.class.getSimpleName()); + mHandlerThread.start(); + } + + final private TextWatcher textWatcher = new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + validateInputs(); + } + }; + + private void validateInputs() { + Integer carbs = SafeParse.stringToInt(editCarbs.getText()); + if (carbs > maxCarbs) { + editCarbs.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.carbsconstraintapplied)); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.overview_newcarbs_dialog, container, false); + + view.findViewById(R.id.ok).setOnClickListener(this); + view.findViewById(R.id.cancel).setOnClickListener(this); + + getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + + maxCarbs = MainApp.getConfigBuilder().applyCarbsConstraints(Constants.carbsOnlyForCheckLimit); + + foodText = view.findViewById(R.id.newcarb_food); + + 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); + + dateButton = view.findViewById(R.id.newcarbs_eventdate); + timeButton = view.findViewById(R.id.newcarb_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); + +// TODO prefilling carbs, maybe +// TODO maybe update suggested carbs to target TT when checked +// APSResult lastAPSResult = ConfigBuilderPlugin.getActiveAPS().getLastAPSResult(); +// if (lastAPSResult != null && lastAPSResult instanceof DetermineBasalResultSMB && ((DetermineBasalResultSMB) lastAPSResult).carbsReq > 0) { +// editCarbs.setValue(((DetermineBasalResultSMB) lastAPSResult).carbsReq); +// } + + fav1Button = view.findViewById(R.id.newcarbs_plus1); + fav1Button.setOnClickListener(this); + fav1Button.setText("+" + SP.getString(MainApp.gs(R.string.key_carbs_button_increment_1), String.valueOf(FAV1_DEFAULT))); + fav2Button = view.findViewById(R.id.newcarbs_plus2); + fav2Button.setOnClickListener(this); + fav2Button.setText("+" + SP.getString(MainApp.gs(R.string.key_carbs_button_increment_2), String.valueOf(FAV2_DEFAULT))); + fav3Button = view.findViewById(R.id.newcarbs_plus3); + fav3Button.setOnClickListener(this); + fav3Button.setText("+" + SP.getString(MainApp.gs(R.string.key_carbs_button_increment_3), String.valueOf(FAV3_DEFAULT))); + + suspendLoopCheckbox = view.findViewById(R.id.newcarbs_suspend_loop); + + setCancelable(true); + getDialog().setCanceledOnTouchOutside(false); + return view; + } + + @Override + public synchronized void onClick(View view) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(eventTime); + switch (view.getId()) { + case R.id.ok: + submit(); + break; + 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(editCarbs.getValue() + + SP.getDouble(MainApp.gs(R.string.key_carbs_button_increment_1), FAV1_DEFAULT)); + validateInputs(); + break; + case R.id.newcarbs_plus2: + editCarbs.setValue(editCarbs.getValue() + + SP.getDouble(MainApp.gs(R.string.key_carbs_button_increment_2), FAV2_DEFAULT)); + validateInputs(); + break; + case R.id.newcarbs_plus3: + editCarbs.setValue(editCarbs.getValue() + + SP.getDouble(MainApp.gs(R.string.key_carbs_button_increment_3), FAV3_DEFAULT)); + validateInputs(); + break; + } + } + + private void submit() { + if (okClicked) { + log.debug("guarding: ok already clicked"); + dismiss(); + return; + } + okClicked = true; + try { + final String food = StringUtils.trimToNull(foodText.getText().toString()); + final Integer carbs = SafeParse.stringToInt(editCarbs.getText()); + Integer carbsAfterConstraints = MainApp.getConfigBuilder().applyCarbsConstraints(carbs); + + String confirmMessage = ""; + if (carbs > 0) + confirmMessage += getString(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""; + if (!carbsAfterConstraints.equals(carbs)) + confirmMessage += "
" + getString(R.string.carbsconstraintapplied) + ""; + if (suspendLoopCheckbox.isChecked()) { + confirmMessage += "
" + "Loop: " + "" + "Suspend for 30 min"; + } + + double prefTTDuration = SP.getDouble(R.string.key_activity_duration, 90d); + double ttDuration = prefTTDuration > 0 ? prefTTDuration : 90d; + double prefTT = SP.getDouble(R.string.key_activity_target, 140d); + double tt = prefTT > 0 ? prefTT : 140d; + if (startActivityTTCheckbox.isChecked()) { + confirmMessage += "
" + "TT: " + "" + ((int) tt) + "mg/dl for " + ((int) ttDuration) + " min "; + } + + if (StringUtils.isNoneEmpty(food)) { + confirmMessage += "
" + "Food: " + food; + } + + if (!initialEventTime.equals(eventTime)) { + confirmMessage += "
Time: " + DateUtil.dateAndTimeString(eventTime); + } + + final int finalCarbsAfterConstraints = carbsAfterConstraints; + + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + + builder.setTitle(this.getContext().getString(R.string.confirmation)); + if (confirmMessage.startsWith("
")) + confirmMessage = confirmMessage.substring("
".length()); + builder.setMessage(Html.fromHtml(confirmMessage)); + builder.setPositiveButton(getString(R.string.ok), (dialog, id) -> { + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; + + if (suspendLoopCheckbox.isChecked()) { + final LoopPlugin activeloop = ConfigBuilderPlugin.getActiveLoop(); + activeloop.suspendTo(System.currentTimeMillis() + 30L * 60 * 1000); + ConfigBuilderPlugin.getCommandQueue().cancelTempBasal(true, new Callback() { + @Override + public void run() { + if (!result.success) { + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.sResources.getString(R.string.tempbasaldeliveryerror)); + } + } + }); + } + + if (startActivityTTCheckbox.isChecked()) { + TempTarget tempTarget = new TempTarget(); + tempTarget.date = System.currentTimeMillis(); + tempTarget.durationInMinutes = (int) ttDuration; + tempTarget.reason = "Activity"; + tempTarget.source = Source.USER; + tempTarget.low = (int) tt; + tempTarget.high = (int) tt; + MainApp.getDbHelper().createOrUpdate(tempTarget); + } + + if (finalCarbsAfterConstraints > 0 || food != null) { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.date = eventTime.getTime(); + detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION; + detailedBolusInfo.carbs = finalCarbsAfterConstraints; +// detailedBolusInfo.food = food; + detailedBolusInfo.context = context; + detailedBolusInfo.source = Source.USER; + MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo); + } + } + }); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.show(); + dismiss(); + } catch (Exception e) { + 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)); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..207518a9c8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java @@ -0,0 +1,342 @@ +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.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; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.TextView; + +import com.crashlytics.android.answers.Answers; +import com.crashlytics.android.answers.CustomEvent; +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 info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalResultSMB; +import info.nightscout.androidaps.queue.Callback; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.NumberPicker; +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 { + 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 recordOnlyCheckbox; + + private Double maxInsulin; + + //one shot guards + private boolean accepted; + private boolean okClicked; + + public NewInsulinDialog() { + HandlerThread mHandlerThread = new HandlerThread(NewInsulinDialog.class.getSimpleName()); + mHandlerThread.start(); + } + + final private TextWatcher textWatcher = new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + validateInputs(); + } + }; + + private void validateInputs() { + Double insulin = SafeParse.stringToDouble(editInsulin.getText()); + if (insulin > maxInsulin) { + editInsulin.setValue(0d); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.bolusconstraintapplied)); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.overview_newinsulin_dialog, container, false); + + view.findViewById(R.id.ok).setOnClickListener(this); + view.findViewById(R.id.cancel).setOnClickListener(this); + + getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + + maxInsulin = MainApp.getConfigBuilder().applyBolusConstraints(Constants.bolusOnlyForCheckLimit); + + editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newinsulin_amount); + + editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getActivePump().getPumpDescription().bolusStep, new DecimalFormat("0.0"), 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); + +/* + // This makes it to easy to just bolus insulinReq, which is almost always too much + APSResult lastAPSResult = ConfigBuilderPlugin.getActiveAPS().getLastAPSResult(); + if (lastAPSResult != null && lastAPSResult instanceof DetermineBasalResultSMB && ((DetermineBasalResultSMB) lastAPSResult).insulinReq > 0) { + editInsulin.setValue(((DetermineBasalResultSMB )lastAPSResult).insulinReq); + } +*/ + + plus1Button = (Button) view.findViewById(R.id.newinsulin_plus05); + plus1Button.setOnClickListener(this); + plus1Button.setText("+" + SP.getString(MainApp.gs(R.string.key_insulin_button_increment_1), String.valueOf(PLUS1_DEFAULT))); + plus2Button = (Button) view.findViewById(R.id.newinsulin_plus10); + plus2Button.setOnClickListener(this); + plus2Button.setText("+" + SP.getString(MainApp.gs(R.string.key_insulin_button_increment_2), String.valueOf(PLUS2_DEFAULT))); + plus3Button = (Button) view.findViewById(R.id.newinsulin_plus20); + plus3Button.setOnClickListener(this); + plus3Button.setText("+" + SP.getString(MainApp.gs(R.string.key_insulin_button_increment_3), String.valueOf(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); + }); + + setCancelable(true); + getDialog().setCanceledOnTouchOutside(false); + return view; + } + + @Override + public synchronized void onClick(View view) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(eventTime); + switch (view.getId()) { + case R.id.ok: + submit(); + break; + 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_start_eating_soon_tt: + final Profile profile = MainApp.getConfigBuilder().getProfile(); + double tt = SP.getDouble(R.string.key_eatingsoon_target, 0d); + double ttBgAdd = (tt - profile.getTargetLow()) / profile.getIsf(); + editInsulin.setValue(editInsulin.getValue() + (startESMCheckbox.isChecked() ? ttBgAdd : -ttBgAdd)); + break; + case R.id.newinsulin_plus05: + editInsulin.setValue(editInsulin.getValue() + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)); + validateInputs(); + break; + case R.id.newinsulin_plus10: + editInsulin.setValue(editInsulin.getValue() + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)); + validateInputs(); + break; + case R.id.newinsulin_plus20: + editInsulin.setValue(editInsulin.getValue() + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)); + validateInputs(); + break; + } + } + + private void submit() { + if (okClicked){ + log.debug("guarding: ok already clicked"); + dismiss(); + return; + } + okClicked = true; + try { + Double insulin = SafeParse.stringToDouble(editInsulin.getText()); + Double insulinAfterConstraints = MainApp.getConfigBuilder().applyBolusConstraints(insulin); + + String confirmMessage = ""; + if (insulin > 0) { + confirmMessage += getString(R.string.bolus) + ": " + "" + insulinAfterConstraints + "U" + ""; + if (recordOnlyCheckbox.isChecked()) { + confirmMessage += "
" + "Bolus will be recorded only"; + } + } + + if (!insulinAfterConstraints.equals(insulin)) + confirmMessage += "
" + getString(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); + double tt = prefTT > 0 ? prefTT : 80d; + if (startESMCheckbox.isChecked()) { + confirmMessage += "
" + "TT: " + "" + ((int) tt) + "mg/dl for " + ((int) ttDuration) + " min "; + } + + if (!initialEventTime.equals(eventTime)) { + confirmMessage += "
Time: " + DateUtil.dateAndTimeString(eventTime); + } + + final double finalInsulinAfterConstraints = insulinAfterConstraints; + + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + + builder.setTitle(this.getContext().getString(R.string.confirmation)); + if (confirmMessage.startsWith("
")) + confirmMessage = confirmMessage.substring("
".length()); + builder.setMessage(Html.fromHtml(confirmMessage)); + builder.setPositiveButton(getString(R.string.ok), (dialog, id) -> { + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; + + if (startESMCheckbox.isChecked()) { + TempTarget tempTarget = new TempTarget(); + tempTarget.date = System.currentTimeMillis(); + tempTarget.durationInMinutes = (int) ttDuration; + tempTarget.reason = "Eating soon"; + tempTarget.source = Source.USER; + tempTarget.low = (int) tt; + tempTarget.high = (int) tt; + MainApp.getDbHelper().createOrUpdate(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; + MainApp.getConfigBuilder().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.sResources.getString(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + }); + Answers.getInstance().logCustom(new CustomEvent("Bolus")); + } + } + }); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.show(); + dismiss(); + } catch (Exception e) { + 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)); + } +} \ No newline at end of file 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 197a73946d..cb8fd76e42 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -3,9 +3,11 @@ package info.nightscout.androidaps.plugins.Overview; import android.annotation.SuppressLint; import android.app.Activity; import android.app.NotificationManager; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.Paint; import android.os.Bundle; @@ -94,6 +96,8 @@ import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastA import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.Overview.Dialogs.CalibrationDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.plugins.Overview.Dialogs.NewCarbsDialog; +import info.nightscout.androidaps.plugins.Overview.Dialogs.NewInsulinDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; import info.nightscout.androidaps.plugins.Overview.activities.QuickWizardListActivity; @@ -116,6 +120,8 @@ import info.nightscout.utils.SP; import info.nightscout.utils.SingleClickButton; import info.nightscout.utils.ToastUtils; +import static java.util.Objects.requireNonNull; + public class OverviewFragment extends Fragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, View.OnLongClickListener { private static Logger log = LoggerFactory.getLogger(OverviewFragment.class); @@ -163,10 +169,14 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, LinearLayoutManager llm; LinearLayout acceptTempLayout; + SingleClickButton acceptTempButton; + SingleClickButton treatmentButton; SingleClickButton wizardButton; SingleClickButton calibrationButton; - SingleClickButton acceptTempButton; + SingleClickButton insulinButton; + SingleClickButton carbsButton; + SingleClickButton cgmButton; SingleClickButton quickWizardButton; CheckBox lockScreen; @@ -253,19 +263,28 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, bgGraph = (GraphView) view.findViewById(R.id.overview_bggraph); iobGraph = (GraphView) view.findViewById(R.id.overview_iobgraph); - treatmentButton = (SingleClickButton) view.findViewById(R.id.overview_treatmentbutton); + treatmentButton = requireNonNull(view.findViewById(R.id.overview_treatmentbutton)); treatmentButton.setOnClickListener(this); - wizardButton = (SingleClickButton) view.findViewById(R.id.overview_wizardbutton); + wizardButton = requireNonNull(view.findViewById(R.id.overview_wizardbutton)); wizardButton.setOnClickListener(this); + insulinButton = (SingleClickButton) view.findViewById(R.id.overview_insulinbutton); + if (insulinButton != null) + insulinButton.setOnClickListener(this); + carbsButton = (SingleClickButton) view.findViewById(R.id.overview_carbsbutton); + if (carbsButton != null) + carbsButton.setOnClickListener(this); acceptTempButton = (SingleClickButton) view.findViewById(R.id.overview_accepttempbutton); if (acceptTempButton != null) acceptTempButton.setOnClickListener(this); - quickWizardButton = (SingleClickButton) view.findViewById(R.id.overview_quickwizardbutton); + quickWizardButton = requireNonNull(view.findViewById(R.id.overview_quickwizardbutton)); quickWizardButton.setOnClickListener(this); quickWizardButton.setOnLongClickListener(this); calibrationButton = (SingleClickButton) view.findViewById(R.id.overview_calibrationbutton); if (calibrationButton != null) calibrationButton.setOnClickListener(this); + cgmButton = (SingleClickButton) view.findViewById(R.id.overview_cgmbutton); + if (cgmButton != null) + cgmButton.setOnClickListener(this); acceptTempLayout = (LinearLayout) view.findViewById(R.id.overview_accepttemplayout); @@ -628,10 +647,19 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, CalibrationDialog calibrationDialog = new CalibrationDialog(); calibrationDialog.show(manager, "CalibrationDialog"); break; + case R.id.overview_cgmbutton: + openXdrip(); + break; case R.id.overview_treatmentbutton: NewTreatmentDialog treatmentDialogFragment = new NewTreatmentDialog(); treatmentDialogFragment.show(manager, "TreatmentDialog"); break; + case R.id.overview_insulinbutton: + new NewInsulinDialog().show(manager, "InsulinDialog"); + break; + case R.id.overview_carbsbutton: + new NewCarbsDialog().show(manager, "CarbsDialog"); + break; case R.id.overview_pumpstatus: if (ConfigBuilderPlugin.getActivePump().isSuspended() || !ConfigBuilderPlugin.getActivePump().isInitialized()) ConfigBuilderPlugin.getCommandQueue().readStatus("RefreshClicked", null); @@ -658,6 +686,25 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } + public boolean openXdrip() { + PackageManager packageManager = getContext().getPackageManager(); + try { + Intent intent = packageManager.getLaunchIntentForPackage("com.eveningoutpost.dexdrip"); + if (intent == null) { + throw new ActivityNotFoundException(); + } + intent.addCategory(Intent.CATEGORY_LAUNCHER); + getContext().startActivity(intent); + return true; + } catch (ActivityNotFoundException e) { + new AlertDialog.Builder(getContext()) + .setMessage(R.string.erro_starting_xdrip) + .setPositiveButton("OK", null) + .show(); + return false; + } + } + @Override public boolean onLongClick(View v) { switch (v.getId()) { @@ -1106,14 +1153,23 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } } - // **** Calibration button **** + // **** Calibration & CGM buttons **** + boolean xDripIsBgSource = MainApp.getSpecificPlugin(SourceXdripPlugin.class) != null && MainApp.getSpecificPlugin(SourceXdripPlugin.class).isEnabled(PluginBase.BGSOURCE); + boolean bgAvailable = DatabaseHelper.actualBg() != null; if (calibrationButton != null) { - if (MainApp.getSpecificPlugin(SourceXdripPlugin.class) != null && MainApp.getSpecificPlugin(SourceXdripPlugin.class).isEnabled(PluginBase.BGSOURCE) && profile != null && DatabaseHelper.actualBg() != null) { + if (xDripIsBgSource && bgAvailable && SP.getBoolean(R.string.key_show_calibration_button, true)) { calibrationButton.setVisibility(View.VISIBLE); } else { calibrationButton.setVisibility(View.GONE); } } + if (cgmButton != null) { + if (xDripIsBgSource && bgAvailable && SP.getBoolean(R.string.key_show_cgm_button, false)) { + cgmButton.setVisibility(View.VISIBLE); + } else { + cgmButton.setVisibility(View.GONE); + } + } final TemporaryBasal activeTemp = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); String basalText = ""; @@ -1210,15 +1266,40 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else quickWizardButton.setVisibility(View.GONE); - // Bolus and calc button - if (pump.isInitialized() && !pump.isSuspended()) { - wizardButton.setVisibility(View.VISIBLE); - treatmentButton.setVisibility(View.VISIBLE); - } else { - wizardButton.setVisibility(View.GONE); - treatmentButton.setVisibility(View.GONE); + // **** Various treatment buttons **** + if (carbsButton != null) { + if (SP.getBoolean(R.string.key_show_carbs_button, false) + && !ConfigBuilderPlugin.getActivePump().getPumpDescription().storesCarbInfo || + (pump.isInitialized() && !pump.isSuspended())) { + carbsButton.setVisibility(View.VISIBLE); + } else { + carbsButton.setVisibility(View.GONE); + } } + if (pump.isInitialized() && !pump.isSuspended()) { + if (treatmentButton != null){ + if (SP.getBoolean(R.string.key_show_treatment_button, false)) { + treatmentButton.setVisibility(View.VISIBLE); + } else { + treatmentButton.setVisibility(View.GONE); + } + } + if (wizardButton != null) { + if (SP.getBoolean(R.string.key_show_wizard_button, false)) { + wizardButton.setVisibility(View.VISIBLE); + } else { + wizardButton.setVisibility(View.GONE); + } + } + if (insulinButton != null) { + if (SP.getBoolean(R.string.key_show_insulin_button, false)) { + insulinButton.setVisibility(View.VISIBLE); + } else { + insulinButton.setVisibility(View.GONE); + } + } + } // **** BG value **** if (lastBG == null) { //left this here as it seems you want to exit at this point if it is null... diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index bc699bad3c..8b7066f081 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -404,7 +404,7 @@ android:drawableTop="@drawable/icon_bolus" android:paddingLeft="0dp" android:paddingRight="0dp" - android:text="@string/overview_bolus_label" + android:text="@string/overview_treatment_label" android:textColor="@color/colorTreatmentButton" android:textSize="10sp" android:visibility="gone" /> @@ -424,6 +424,36 @@ android:textSize="10sp" android:visibility="gone" /> + + + + + + @@ -643,6 +643,36 @@ android:textSize="10sp" android:visibility="gone" /> + + + + + diff --git a/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml b/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml index 0b66f79ab2..59589bf31a 100644 --- a/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml +++ b/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml @@ -707,7 +707,7 @@ android:drawableTop="@drawable/icon_bolus" android:paddingLeft="0dp" android:paddingRight="0dp" - android:text="@string/overview_bolus_label" + android:text="@string/overview_treatment_label" android:textColor="@color/colorTreatmentButton" android:textSize="10sp" /> @@ -726,6 +726,35 @@ android:textSize="10sp" android:visibility="gone" /> + + + + + + - @@ -412,6 +411,35 @@ android:textSize="10sp" android:visibility="gone" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +