diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bc900d0b40..8d8c9baf08 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" - android:process=":mainProcess" android:theme="@style/AppTheme"> @@ -30,12 +29,11 @@ - + + android:exported="true"> diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index ffd95db125..8788020c87 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -15,12 +15,15 @@ import org.slf4j.LoggerFactory; import java.util.List; import info.nightscout.androidaps.plugins.ProfileViewer.ProfileViewerFragment; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment; import info.nightscout.androidaps.tabs.*; import info.nightscout.androidaps.plugins.Objectives.ObjectivesFragment; import info.nightscout.androidaps.plugins.Test.TestFragment; import info.nightscout.client.broadcasts.Intents; -public class MainActivity extends AppCompatActivity implements ObjectivesFragment.OnFragmentInteractionListener { +public class MainActivity extends AppCompatActivity + implements ObjectivesFragment.OnFragmentInteractionListener, + TreatmentsFragment.OnFragmentInteractionListener { private static Logger log = LoggerFactory.getLogger(MainActivity.class); private Toolbar toolbar; @@ -36,6 +39,7 @@ public class MainActivity extends AppCompatActivity implements ObjectivesFragmen // Register all tabs in app here mAdapter = new TabPageAdapter(getSupportFragmentManager()); mAdapter.registerNewFragment("Test", TestFragment.newInstance()); + mAdapter.registerNewFragment("Treatments", TreatmentsFragment.newInstance()); mAdapter.registerNewFragment("Profile", ProfileViewerFragment.newInstance()); mAdapter.registerNewFragment("Objectives", ObjectivesFragment.newInstance()); diff --git a/app/src/main/java/info/nightscout/androidaps/data/Iob.java b/app/src/main/java/info/nightscout/androidaps/data/Iob.java new file mode 100644 index 0000000000..ee70699604 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/Iob.java @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.data; + +/** + * Created by mike on 05.06.2016. + */ +public class Iob { + public double iobContrib = 0d; + public double activityContrib = 0d; + + public Iob plus(Iob iob) { + iobContrib += iob.iobContrib; + activityContrib += iob.activityContrib; + return this; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index ce60c2dc53..b09714ab62 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -16,8 +16,10 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; +import info.nightscout.androidaps.data.Iob; import info.nightscout.client.broadcasts.Intents; import info.nightscout.androidaps.MainApp; +import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DateUtil; @DatabaseTable(tableName = "Treatments") @@ -53,40 +55,43 @@ public class Treatment { this.insulin = t.insulin; this.carbs = t.carbs; } -/* - public Iob iobCalc(Date time, Double dia) { - Double diaratio = 3.0 / dia; - Double peak = 75d; - Double end = 180d; - //var sens = profile_data.sens; - Iob results = new Iob(); + public Iob iobCalc(Date time) { - if (insulin != 0) { - long bolusTime = created_at.getTime(); - Double minAgo = diaratio * (time.getTime() - bolusTime) / 1000 / 60; - Double iobContrib = 0d; - Double activityContrib = 0d; + Iob result = new Iob(); + NSProfile profile = MainApp.getNSProfile(); - if (minAgo < peak) { - Double x = (minAgo/5 + 1); - iobContrib = insulin * (1 - 0.001852 * x * x + 0.001852 * x); - //activityContrib=sens*treatment.insulin*(2/dia/60/peak)*minAgo; - activityContrib = insulin * (2 / dia / 60 / peak) * minAgo; - } else if (minAgo < end) { - Double y = (minAgo-peak)/5; - iobContrib = insulin * (0.001323 * y * y - .054233 * y + .55556); - //activityContrib=sens*treatment.insulin*(2/dia/60-(minAgo-peak)*2/dia/60/(60*dia-peak)); - activityContrib = insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * dia - peak)); - } - - results.iobContrib = iobContrib; - results.activityContrib = activityContrib; + if (profile == null) { + return result; } - return results; + Double dia = profile.getDia(); + Double sens = profile.getIsf(profile.secondsFromMidnight(time)); + + Double scaleFactor = 3.0 / dia; + Double peak = 75d; + Double end = 180d; + + if (this.insulin != 0d) { + Long bolusTime = this.created_at.getTime(); + Double minAgo = scaleFactor * (time.getTime() - bolusTime) / 1000d / 60d; + + if (minAgo < peak) { + Double x1 = minAgo / 5 + 1; + result.iobContrib = this.insulin * (1 - 0.001852 * x1 * x1 + 0.001852 * x1); + // units: BG (mg/dL) = (BG/U) * U insulin * scalar + result.activityContrib = sens * this.insulin * (2 / dia / 60 / peak) * minAgo; + + } else if (minAgo < end) { + Double x2 = (minAgo - 75) / 5; + result.iobContrib = this.insulin * (0.001323 * x2 * x2 - 0.054233 * x2 + 0.55556); + result.activityContrib = sens * this.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * dia - peak)); + } + } + return result; } +/* public Iob calcIobOpenAPS() { IobCalc calc = new IobCalc(created_at,insulin,new Date()); calc.setBolusDiaTimesTwo(); diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java new file mode 100644 index 0000000000..2d6454406a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 05.06.2016. + */ +public class EventNewBG { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java index 4ff01f1de3..747733913e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java @@ -91,6 +91,4 @@ public class ProfileViewerFragment extends Fragment { public void onStatusEvent(final EventNewBasalProfile ev) { setContent(); } - - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java new file mode 100644 index 0000000000..23fbe175ad --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -0,0 +1,237 @@ +package info.nightscout.androidaps.plugins.Treatments; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.CardView; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.events.EventNewBG; +import info.nightscout.androidaps.events.EventNewBasalProfile; +import info.nightscout.androidaps.events.EventTreatmentChange; + +public class TreatmentsFragment extends Fragment { + private static Logger log = LoggerFactory.getLogger(TreatmentsFragment.class); + + RecyclerView recyclerView; + LinearLayoutManager llm; + + TextView iobTotal; + TextView activityTotal; + + private static DecimalFormat formatNumber0decimalplaces = new DecimalFormat("0"); + private static DecimalFormat formatNumber2decimalplaces = new DecimalFormat("0.00"); + private static DecimalFormat formatNumber3decimalplaces = new DecimalFormat("0.000"); + + private OnFragmentInteractionListener mListener; + + private List treatments; + + private void initializeData() { + try { + Dao dao = MainApp.getDbHelper().getDaoTreatments(); + QueryBuilder queryBuilder = dao.queryBuilder(); + queryBuilder.orderBy("timeIndex", false); + queryBuilder.limit(30l); + PreparedQuery preparedQuery = queryBuilder.prepare(); + treatments = dao.query(preparedQuery); + } catch (SQLException e) { + log.debug(e.getMessage(), e); + treatments = new ArrayList(); + } + if (recyclerView != null) { + recyclerView.swapAdapter(new RecyclerViewAdapter(treatments), false); + //recyclerView.getAdapter().notifyDataSetChanged(); + } + updateTotalIOB(); + } + + private void updateTotalIOB() { + Iob total = new Iob(); + for (Integer pos = 0; pos < treatments.size(); pos++ ) { + Treatment t = treatments.get(pos); + total.plus(t.iobCalc(new Date())); + } + if (iobTotal != null) + iobTotal.setText(formatNumber2decimalplaces.format(total.iobContrib)); + if (activityTotal != null) + activityTotal.setText(formatNumber3decimalplaces.format(total.activityContrib)); + } + + public static class RecyclerViewAdapter extends RecyclerView.Adapter { + + List treatments; + + RecyclerViewAdapter(List treatments) { + this.treatments = treatments; + } + + @Override + public TreatmentsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_item, viewGroup, false); + TreatmentsViewHolder treatmentsViewHolder = new TreatmentsViewHolder(v); + return treatmentsViewHolder; + } + + @Override + public void onBindViewHolder(TreatmentsViewHolder holder, int position) { + // TODO: implement locales + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, new Locale("cs", "CZ")); + holder.date.setText(df.format(treatments.get(position).created_at)); + holder.insulin.setText(formatNumber2decimalplaces.format(treatments.get(position).insulin) + " U"); + holder.carbs.setText(formatNumber0decimalplaces.format(treatments.get(position).carbs) + " g"); + Iob iob = treatments.get(position).iobCalc(new Date()); + holder.iob.setText(formatNumber2decimalplaces.format(iob.iobContrib) + " U"); + holder.activity.setText(formatNumber3decimalplaces.format(iob.activityContrib) + " U"); + } + + @Override + public int getItemCount() { + return treatments.size(); + } + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + } + + public static class TreatmentsViewHolder extends RecyclerView.ViewHolder { + CardView cv; + TextView date; + TextView insulin; + TextView carbs; + TextView iob; + TextView activity; + + TreatmentsViewHolder(View itemView) { + super(itemView); + cv = (CardView) itemView.findViewById(R.id.treatments_cardview); + date = (TextView) itemView.findViewById(R.id.treatments_date); + insulin = (TextView) itemView.findViewById(R.id.treatments_insulin); + carbs = (TextView) itemView.findViewById(R.id.treatments_carbs); + iob = (TextView) itemView.findViewById(R.id.treatments_iob); + activity = (TextView) itemView.findViewById(R.id.treatments_activity); + } + } + } + + public TreatmentsFragment() { + super(); + initializeData(); + } + + public static TreatmentsFragment newInstance() { + TreatmentsFragment fragment = new TreatmentsFragment(); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + registerBus(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.treatments_fragment, container, false); + + recyclerView = (RecyclerView) view.findViewById(R.id.treatments_recyclerview); + recyclerView.setHasFixedSize(true); + llm = new LinearLayoutManager(view.getContext()); + recyclerView.setLayoutManager(llm); + + RecyclerViewAdapter adapter = new RecyclerViewAdapter(treatments); + recyclerView.setAdapter(adapter); + + iobTotal = (TextView) view.findViewById(R.id.treatments_iobtotal); + activityTotal = (TextView) view.findViewById(R.id.treatments_iobactivitytotal); + + return view; + } + + /* + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + */ + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + private void registerBus() { + try { + MainApp.bus().unregister(this); + } catch (RuntimeException x) { + // Ignore + } + MainApp.bus().register(this); + } + + @Subscribe + public void onStatusEvent(final EventTreatmentChange ev) { + initializeData(); + } + + @Subscribe + public void onStatusEvent(final EventNewBG ev) { + updateTotalIOB(); + recyclerView.getAdapter().notifyDataSetChanged(); + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(String param); + } +} diff --git a/app/src/main/java/info/nightscout/client/data/NSProfile.java b/app/src/main/java/info/nightscout/client/data/NSProfile.java index 30e0ba3594..9965d1db4e 100644 --- a/app/src/main/java/info/nightscout/client/data/NSProfile.java +++ b/app/src/main/java/info/nightscout/client/data/NSProfile.java @@ -6,6 +6,7 @@ import org.json.JSONObject; import java.text.DecimalFormat; import java.util.Calendar; +import java.util.Date; import java.util.TimeZone; public class NSProfile { @@ -279,4 +280,16 @@ public class NSProfile { long passed = now - c.getTimeInMillis(); return (int) (passed / 1000); } + + public static int secondsFromMidnight(Date date) { + Calendar c = Calendar.getInstance(); + long now = date.getTime(); + c.setTime(date); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + long passed = now - c.getTimeInMillis(); + return (int) (passed / 1000); + } } diff --git a/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java b/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java index 3576e004cd..91756deffc 100644 --- a/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java +++ b/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java @@ -21,6 +21,7 @@ import java.sql.SQLException; import java.util.Date; import java.util.List; +import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.client.broadcasts.Intents; @@ -182,6 +183,7 @@ public class NSClientDataReceiver extends BroadcastReceiver { removeTreatmentFromDb(_id); } } + MainApp.bus().post(new EventTreatmentChange()); } catch (JSONException e) { e.printStackTrace(); @@ -189,6 +191,10 @@ public class NSClientDataReceiver extends BroadcastReceiver { e1.printStackTrace(); } } + + if (intent.getAction().equals(Intents.ACTION_NEW_SGV)) { + MainApp.bus().post(new EventNewBG()); + } } public void storeNSProfile() { diff --git a/app/src/main/res/layout/treatments_fragment.xml b/app/src/main/res/layout/treatments_fragment.xml new file mode 100644 index 0000000000..578e1b3900 --- /dev/null +++ b/app/src/main/res/layout/treatments_fragment.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/treatments_item.xml b/app/src/main/res/layout/treatments_item.xml new file mode 100644 index 0000000000..902990d0b1 --- /dev/null +++ b/app/src/main/res/layout/treatments_item.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 12b1f8b0ed..5e866eff2f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,5 +34,11 @@ Basal: Target: NO PROFILE SET + Insulin: + Carbs: + IOB: + Activity: + Toal IOB: + Total IOB activity: