From 629ed62e0350aa5fef9311cf5b9e237dba23ac25 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 5 Jun 2016 01:40:35 +0200 Subject: [PATCH 001/336] Profile fragment --- .idea/misc.xml | 2 +- app/build.gradle | 10 +- app/src/main/AndroidManifest.xml | 34 ++- app/src/main/assets/logback.xml | 35 +++ .../nightscout/androidaps/MainActivity.java | 26 ++ .../info/nightscout/androidaps/MainApp.java | 109 +++++++ .../info/nightscout/androidaps/data/Pump.java | 10 + .../androidaps/db/DatabaseHelper.java | 92 ++++++ .../nightscout/androidaps/db/TempBasal.java | 132 ++++++++ .../nightscout/androidaps/db/Treatment.java | 173 +++++++++++ .../events/EventNewBasalProfile.java | 8 + .../events/EventTreatmentChange.java | 7 + .../Objectives/ObjectivesFragment.java | 3 +- .../ProfileViewer/ProfileViewerFragment.java | 96 ++++++ .../androidaps/plugins/Test/TestFragment.java | 2 +- .../nightscout/client/broadcasts/Intents.java | 15 + .../info/nightscout/client/data/NSCal.java | 22 ++ .../nightscout/client/data/NSProfile.java | 282 ++++++++++++++++++ .../info/nightscout/client/data/NSSgv.java | 63 ++++ .../info/nightscout/client/data/NSStatus.java | 105 +++++++ .../nightscout/client/data/NSTreatment.java | 89 ++++++ .../receivers/NSClientDataReceiver.java | 258 ++++++++++++++++ .../java/info/nightscout/utils/DateUtil.java | 52 ++++ ...objectives.xml => objectives_fragment.xml} | 0 app/src/main/res/layout/objectives_item.xml | 2 +- .../res/layout/profileviewer_fragment.xml | 106 +++++++ .../{app_fragment.xml => test_fragment.xml} | 0 app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 7 + 29 files changed, 1733 insertions(+), 9 deletions(-) create mode 100644 app/src/main/assets/logback.xml create mode 100644 app/src/main/java/info/nightscout/androidaps/MainApp.java create mode 100644 app/src/main/java/info/nightscout/androidaps/data/Pump.java create mode 100644 app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java create mode 100644 app/src/main/java/info/nightscout/androidaps/db/TempBasal.java create mode 100644 app/src/main/java/info/nightscout/androidaps/db/Treatment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java create mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java create mode 100644 app/src/main/java/info/nightscout/client/broadcasts/Intents.java create mode 100644 app/src/main/java/info/nightscout/client/data/NSCal.java create mode 100644 app/src/main/java/info/nightscout/client/data/NSProfile.java create mode 100644 app/src/main/java/info/nightscout/client/data/NSSgv.java create mode 100644 app/src/main/java/info/nightscout/client/data/NSStatus.java create mode 100644 app/src/main/java/info/nightscout/client/data/NSTreatment.java create mode 100644 app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java create mode 100644 app/src/main/java/info/nightscout/utils/DateUtil.java rename app/src/main/res/layout/{fragment_objectives.xml => objectives_fragment.xml} (100%) create mode 100644 app/src/main/res/layout/profileviewer_fragment.xml rename app/src/main/res/layout/{app_fragment.xml => test_fragment.xml} (100%) diff --git a/.idea/misc.xml b/.idea/misc.xml index 7158618b85..cca2cdae11 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 5913c26f17..9a7c70ac19 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,5 +24,11 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:support-v4:23.4.0' - compile 'com.android.support:cardview-v7:23.0.+' - compile 'com.android.support:recyclerview-v7:23.0.+'} + compile 'com.android.support:cardview-v7:23.4.0' + compile 'com.android.support:recyclerview-v7:23.4.0' + compile 'com.squareup:otto:1.3.7' + compile 'com.j256.ormlite:ormlite-core:4.46' + compile 'com.j256.ormlite:ormlite-android:4.46' + compile 'com.github.tony19:logback-android-classic:1.1.1-4' + compile 'org.slf4j:slf4j-api:1.7.12' +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c1d6cc617..bc900d0b40 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,19 +2,51 @@ + + + + + + + + + + + + + - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml new file mode 100644 index 0000000000..f6d9d15eae --- /dev/null +++ b/app/src/main/assets/logback.xml @@ -0,0 +1,35 @@ + + + + /storage/sdcard0/AndroidAPS/AndroidAPS.log + + + /storage/sdcard0/AndroidAPS/AndroidAPS._%d{yyyy-MM-dd}.%i.log + + + 5MB + + + 120 + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + %logger{0} + + + [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 74ef27f028..ffd95db125 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps; +import android.content.Intent; +import android.content.pm.ResolveInfo; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; @@ -7,11 +9,19 @@ import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.plugins.ProfileViewer.ProfileViewerFragment; 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 { + private static Logger log = LoggerFactory.getLogger(MainActivity.class); private Toolbar toolbar; private SlidingTabLayout mTabs; @@ -26,6 +36,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("Profile", ProfileViewerFragment.newInstance()); mAdapter.registerNewFragment("Objectives", ObjectivesFragment.newInstance()); toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -34,7 +45,11 @@ public class MainActivity extends AppCompatActivity implements ObjectivesFragmen mPager.setAdapter(mAdapter); mTabs = (SlidingTabLayout) findViewById(R.id.tabs); mTabs.setViewPager(mPager); + + registerBus(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); @@ -53,4 +68,15 @@ public class MainActivity extends AppCompatActivity implements ObjectivesFragmen public void onFragmentInteraction(String param) { } + + private void registerBus() { + try { + MainApp.bus().unregister(this); + } catch (RuntimeException x) { + // Ignore + } + MainApp.bus().register(this); + } + + } diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java new file mode 100644 index 0000000000..3b861c5b63 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -0,0 +1,109 @@ +package info.nightscout.androidaps; + +import android.app.Application; +import android.content.SharedPreferences; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.squareup.otto.Bus; +import com.squareup.otto.ThreadEnforcer; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; + +import info.nightscout.androidaps.data.Pump; +import info.nightscout.client.data.NSProfile; +import info.nightscout.androidaps.db.DatabaseHelper; + + +public class MainApp extends Application { + private static Logger log = LoggerFactory.getLogger(MainApp.class); + + public static final String PREFS_NAME = "NightscoutProfile"; + + + private static Bus sBus; + private static MainApp sInstance; + + private static NSProfile nsProfile = null; + private static String activeProfile = null; + + private static Pump activePump = null; + + private static DatabaseHelper databaseHelper = null; + + @Override + public void onCreate() { + super.onCreate(); + + sBus = new Bus(ThreadEnforcer.ANY); + sInstance = this; + + log.debug("Loading stored profile"); + SharedPreferences store = getSharedPreferences(PREFS_NAME, 0); + activeProfile = store.getString("activeProfile", null); + String profileString = store.getString("profile", null); + if (profileString != null) { + try { + log.debug("Loaded profile: " + profileString); + log.debug("Loaded active profile: " + activeProfile); + setNSProfile(new NSProfile(new JSONObject(profileString), activeProfile)); + } catch (JSONException e) { + } + } else + log.debug("Stored profile not found"); + + } + + public static Bus bus() { + return sBus; + } + public static MainApp instance() { + return sInstance; + } + + public static DatabaseHelper getDbHelper() { + if (databaseHelper == null) { + databaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); + } + return databaseHelper; + } + + public static void closeDbHelper() { + if (databaseHelper != null) { + databaseHelper.close(); + databaseHelper = null; + } + } + + @Override + public void onTerminate() { + super.onTerminate(); + databaseHelper.close(); + } + + public static NSProfile getNSProfile() { + return nsProfile; + } + public static void setNSProfile(NSProfile profile) { + nsProfile = profile; + } + + public static String getActiveProfile() { + return activeProfile; + } + public static void setActiveProfile(String activeprofile) { + activeProfile = activeprofile; + } + + public static Pump getActivePump() { + return activePump; + } + public static void setActivePump(Pump activepump) { + activePump = activepump; + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/data/Pump.java b/app/src/main/java/info/nightscout/androidaps/data/Pump.java new file mode 100644 index 0000000000..b17739e1fa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/Pump.java @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.data; + +/** + * Created by mike on 04.06.2016. + */ +public abstract class Pump { + + // Upload to pump new basal profile from MainApp.getNSProfile() + public abstract void setNewBasalProfile(); +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java new file mode 100644 index 0000000000..3c9a4886a3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -0,0 +1,92 @@ +package info.nightscout.androidaps.db; + +import java.sql.SQLException; +import java.util.List; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; + +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DatabaseHelper extends OrmLiteSqliteOpenHelper { + private static Logger log = LoggerFactory.getLogger(DatabaseHelper.class); + + public static final String DATABASE_NAME = "AndroidAPSDb"; + + private static final int DATABASE_VERSION = 1; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + + @Override + public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { + try { + log.info("onCreate"); + TableUtils.createTableIfNotExists(connectionSource, TempBasal.class); + TableUtils.createTableIfNotExists(connectionSource, Treatment.class); + // TODO: add bg support + } catch (SQLException e) { + log.error(DatabaseHelper.class.getName(), "Can't create database", e); + throw new RuntimeException(e); + } + } + + @Override + public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { + try { + log.info(DatabaseHelper.class.getName(), "onUpgrade"); + TableUtils.dropTable(connectionSource, TempBasal.class, true); + TableUtils.dropTable(connectionSource, Treatment.class, true); + onCreate(database, connectionSource); + } catch (SQLException e) { + log.error(DatabaseHelper.class.getName(), "Can't drop databases", e); + throw new RuntimeException(e); + } + } + + /** + * Close the database connections and clear any cached DAOs. + */ + @Override + public void close() { + super.close(); + } + + public void resetDatabases() { + try { + TableUtils.dropTable(connectionSource, TempBasal.class, true); + TableUtils.dropTable(connectionSource, Treatment.class, true); + TableUtils.createTableIfNotExists(connectionSource, TempBasal.class); + TableUtils.createTableIfNotExists(connectionSource, Treatment.class); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void resetTreatments() { + try { + + TableUtils.dropTable(connectionSource, Treatment.class, true); + TableUtils.createTableIfNotExists(connectionSource, Treatment.class); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Dao getDaoTempBasals() throws SQLException { + return getDao(TempBasal.class); + } + + public Dao getDaoTreatments() throws SQLException { + return getDao(Treatment.class); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java new file mode 100644 index 0000000000..7ecd5b85e0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java @@ -0,0 +1,132 @@ +package info.nightscout.androidaps.db; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +@DatabaseTable(tableName = "TempBasals") +public class TempBasal { + private static Logger log = LoggerFactory.getLogger(TempBasal.class); + + public long getTimeIndex() { + return (long) Math.ceil(timeStart.getTime() / 60000d); + } + + public void setTimeIndex(long timeIndex) { + this.timeIndex = timeIndex; + } + + @DatabaseField(id = true, useGetSet = true) + public long timeIndex; + + + @DatabaseField + public Date timeStart; + + @DatabaseField + public Date timeEnd; + + @DatabaseField + public int percent; // In % of current basal + + @DatabaseField + public int absolute; // Absolute value in U + + @DatabaseField + public int duration; // in minutes + + @DatabaseField + public boolean isExtended; // true if set as extended bolus + + @DatabaseField + public boolean isAbsolute; // true if if set as absolute value in U + +/* + public Iob calcIob() { + Iob iob = new Iob(); + + long msAgo = getMillisecondsFromStart(); + Calendar startAdjusted = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + startAdjusted.setTime(this.timeStart); + int minutes = startAdjusted.get(Calendar.MINUTE); + minutes = minutes % 4; + if (startAdjusted.get(Calendar.SECOND) > 0 && minutes == 0) { + minutes += 4; + } + startAdjusted.add(Calendar.MINUTE, minutes); + startAdjusted.set(Calendar.SECOND, 0); + + IobCalc iobCalc = new IobCalc(); + iobCalc.setTime(new Date()); + iobCalc.setAmount(-1.0d * (baseRatio - tempRatio) / 15.0d / 100.0d); + + long timeStartTime = startAdjusted.getTimeInMillis(); + Date currentTimeEnd = timeEnd; + if (currentTimeEnd == null) { + currentTimeEnd = new Date(); + if (getPlannedTimeEnd().getTime() < currentTimeEnd.getTime()) { + currentTimeEnd = getPlannedTimeEnd(); + } + } + for (long time = timeStartTime; time < currentTimeEnd.getTime(); time += 4 * 60_000) { + Date start = new Date(time); + + iobCalc.setTimeStart(start); + iob.plus(iobCalc.invoke()); + } + + if (Settings.logTempIOBCalculation) { + log.debug("TempIOB start: " + this.timeStart + " end: " + this.timeEnd + " Percent: " + this.percent + " Duration: " + this.duration + " CalcDurat: " + (int) ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) + + "min minAgo: " + (int) (msAgo / 1000 / 60) + " IOB: " + iob.iobContrib + " Activity: " + iob.activityContrib + " Impact: " + (-0.01d * (baseRatio - tempRatio) * ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) / 60) + ); + } + + return iob; + } +*/ + // Determine end of basal + public Date getTimeEnd() { + Date tempBasalTimePlannedEnd = getPlannedTimeEnd(); + + // End already exists in database + if (timeEnd != null) { + return timeEnd; + } + + // if not return planned time + return tempBasalTimePlannedEnd; + } + + public Date getPlannedTimeEnd() { + return new Date(timeStart.getTime() + 60 * 1_000 * duration); + } + + public long getMillisecondsFromStart() { + return new Date().getTime() - timeStart.getTime(); + } + + public int getRemainingMinutes() { + long remainingMin = (getTimeEnd().getTime() - new Date().getTime()) / 1000 / 60; + return (remainingMin < 0) ? 0 : (int) remainingMin; + } + + @Override + public String toString() { + return "TempBasal{" + + "timeIndex=" + timeIndex + + ", timeStart=" + timeStart + + ", timeEnd=" + timeEnd + + ", percent=" + percent + + ", absolute=" + absolute + + ", duration=" + duration + + ", isAbsolute=" + isAbsolute + + ", isExtended=" + isExtended + + '}'; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java new file mode 100644 index 0000000000..ce60c2dc53 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -0,0 +1,173 @@ +package info.nightscout.androidaps.db; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.List; + +import info.nightscout.client.broadcasts.Intents; +import info.nightscout.androidaps.MainApp; +import info.nightscout.utils.DateUtil; + +@DatabaseTable(tableName = "Treatments") +public class Treatment { + private static Logger log = LoggerFactory.getLogger(Treatment.class); + + public long getTimeIndex() { + return (long) Math.ceil(created_at.getTime() / 60000d); + } + + public void setTimeIndex(long timeIndex) { + this.timeIndex = timeIndex; + } + + @DatabaseField(id = true, useGetSet = true) + public long timeIndex; + + @DatabaseField + public String _id; + + @DatabaseField + public Date created_at; + + @DatabaseField + public Double insulin; + + @DatabaseField + public Double carbs; + + public void copyFrom(Treatment t) { + this._id = t._id; + this.created_at = t.created_at; + 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(); + + if (insulin != 0) { + long bolusTime = created_at.getTime(); + Double minAgo = diaratio * (time.getTime() - bolusTime) / 1000 / 60; + Double iobContrib = 0d; + Double activityContrib = 0d; + + 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; + } + + return results; + } + + public Iob calcIobOpenAPS() { + IobCalc calc = new IobCalc(created_at,insulin,new Date()); + calc.setBolusDiaTimesTwo(); + Iob iob = calc.invoke(); + + return iob; + } + public Iob calcIob() { + IobCalc calc = new IobCalc(created_at,insulin,new Date()); + Iob iob = calc.invoke(); + + return iob; + } +*/ + + public long getMillisecondsFromStart() { + return new Date().getTime() - created_at.getTime(); + } + + public String log() { + return "Treatment{" + + "timeIndex: " + timeIndex + + ", _id: " + _id + + ", insulin: " + insulin + + ", carbs: " + carbs + + ", created_at: " + + "}"; + } + + public void sendToNSClient() { + Context context = MainApp.instance().getApplicationContext(); + Bundle bundle = new Bundle(); + bundle.putString("action", "dbAdd"); + bundle.putString("collection", "treatments"); + JSONObject data = new JSONObject(); + try { + data.put("eventType", "Meal Bolus"); + if (insulin != 0d) data.put("insulin", insulin); + if (carbs != 0d) data.put("carbs", carbs.intValue()); + data.put("created_at", DateUtil.toISOString(created_at)); + data.put("timeIndex", timeIndex); + } catch (JSONException e) { + e.printStackTrace(); + } + bundle.putString("data", data.toString()); + Intent intent = new Intent(Intents.ACTION_DATABASE); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List q = context.getPackageManager().queryBroadcastReceivers(intent, 0); + if (q.size() < 1) { + log.error("DBADD No receivers"); + } else log.debug("DBADD dbAdd " + q.size() + " receivers " + data.toString()); + } + + public void updateToNSClient() { + Context context = MainApp.instance().getApplicationContext(); + Bundle bundle = new Bundle(); + bundle.putString("action", "dbUpdate"); + bundle.putString("collection", "treatments"); + JSONObject data = new JSONObject(); + try { + data.put("eventType", "Meal Bolus"); + data.put("insulin", insulin); + data.put("carbs", carbs.intValue()); + data.put("created_at", DateUtil.toISOString(created_at)); + data.put("timeIndex", timeIndex); + } catch (JSONException e) { + e.printStackTrace(); + } + bundle.putString("data", data.toString()); + bundle.putString("_id", _id); + Intent intent = new Intent(Intents.ACTION_DATABASE); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List q = context.getPackageManager().queryBroadcastReceivers(intent, 0); + if (q.size() < 1) { + log.error("DBUPDATE No receivers"); + } else log.debug("DBUPDATE dbUpdate " + q.size() + " receivers " + _id + " " + data.toString()); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java new file mode 100644 index 0000000000..5e74e80d03 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 04.06.2016. + */ +public class EventNewBasalProfile { + // TODO: implement proper GUI update +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java new file mode 100644 index 0000000000..a8597189bc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 04.06.2016. + */ +public class EventTreatmentChange { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Objectives/ObjectivesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Objectives/ObjectivesFragment.java index 1e073c73a3..b80b1c98d3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Objectives/ObjectivesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Objectives/ObjectivesFragment.java @@ -14,7 +14,6 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.StringTokenizer; import info.nightscout.androidaps.R; @@ -139,7 +138,7 @@ public class ObjectivesFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_objectives, container, false); + View view = inflater.inflate(R.layout.objectives_fragment, container, false); recyclerView = (RecyclerView) view.findViewById(R.id.objectives_recyclerview); recyclerView.setHasFixedSize(true); 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 new file mode 100644 index 0000000000..4ff01f1de3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileViewer/ProfileViewerFragment.java @@ -0,0 +1,96 @@ +package info.nightscout.androidaps.plugins.ProfileViewer; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import java.text.DecimalFormat; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventNewBasalProfile; +import info.nightscout.client.data.NSProfile; + +public class ProfileViewerFragment extends Fragment { + private static TextView noProfile; + private static TextView dia; + private static TextView activeProfile; + private static TextView ic; + private static TextView isf; + private static TextView basal; + private static TextView target; + + private static DecimalFormat formatNumber2decimalplaces = new DecimalFormat("0.00"); + + public ProfileViewerFragment() { + } + + public static ProfileViewerFragment newInstance(String param1, String param2) { + ProfileViewerFragment fragment = new ProfileViewerFragment(); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + registerBus(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View layout = inflater.inflate(R.layout.profileviewer_fragment, container, false); + + noProfile = (TextView) layout.findViewById(R.id.profileview_noprofile); + dia = (TextView) layout.findViewById(R.id.profileview_dia); + activeProfile = (TextView) layout.findViewById(R.id.profileview_activeprofile); + ic = (TextView) layout.findViewById(R.id.profileview_ic); + isf = (TextView) layout.findViewById(R.id.profileview_isf); + basal = (TextView) layout.findViewById(R.id.profileview_basal); + target = (TextView) layout.findViewById(R.id.profileview_target); + + setContent(); + return layout; + } + + public static ProfileViewerFragment newInstance() { + ProfileViewerFragment fragment = new ProfileViewerFragment(); + return fragment; + } + + private void setContent() { + NSProfile profile = MainApp.getNSProfile(); + if (profile == null) { + noProfile.setVisibility(View.VISIBLE); + } else { + noProfile.setVisibility(View.GONE); + } + dia.setText(formatNumber2decimalplaces.format(profile.getDia()) + " h"); + activeProfile.setText(profile.getActiveProfile()); + ic.setText(profile.getIcList()); + isf.setText(profile.getIsfList()); + basal.setText(profile.getBasalList()); + target.setText(profile.getTargetList()); + } + + private void registerBus() { + try { + MainApp.bus().unregister(this); + } catch (RuntimeException x) { + // Ignore + } + MainApp.bus().register(this); + } + + @Subscribe + public void onStatusEvent(final EventNewBasalProfile ev) { + setContent(); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Test/TestFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Test/TestFragment.java index 7fa23e3890..1b3b57ca1f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Test/TestFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Test/TestFragment.java @@ -30,7 +30,7 @@ public class TestFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstance) { - View layout = inflater.inflate(R.layout.app_fragment, container, false); + View layout = inflater.inflate(R.layout.test_fragment, container, false); textView = (TextView) layout.findViewById(R.id.position); Bundle bundle = getArguments(); if (bundle != null) { diff --git a/app/src/main/java/info/nightscout/client/broadcasts/Intents.java b/app/src/main/java/info/nightscout/client/broadcasts/Intents.java new file mode 100644 index 0000000000..f667b048ed --- /dev/null +++ b/app/src/main/java/info/nightscout/client/broadcasts/Intents.java @@ -0,0 +1,15 @@ +package info.nightscout.client.broadcasts; + +public interface Intents { + // NSClient -> App + String ACTION_NEW_TREATMENT = "info.nightscout.client.NEW_TREATMENT"; + String ACTION_CHANGED_TREATMENT = "info.nightscout.client.CHANGED_TREATMENT"; + String ACTION_REMOVED_TREATMENT = "info.nightscout.client.REMOVED_TREATMENT"; + String ACTION_NEW_PROFILE = "info.nightscout.client.NEW_PROFILE"; + String ACTION_NEW_SGV = "info.nightscout.client.NEW_SGV"; + String ACTION_NEW_STATUS = "info.nightscout.client.NEW_STATUS"; + + + // App -> NSClient + String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; +} diff --git a/app/src/main/java/info/nightscout/client/data/NSCal.java b/app/src/main/java/info/nightscout/client/data/NSCal.java new file mode 100644 index 0000000000..5a78dbaa02 --- /dev/null +++ b/app/src/main/java/info/nightscout/client/data/NSCal.java @@ -0,0 +1,22 @@ +package info.nightscout.client.data; + +import org.json.JSONException; +import org.json.JSONObject; + +public class NSCal { + public long date; + public double slope; + public double intercept; + public double scale = 1; + + public void set(JSONObject json) { + try { + date = json.getLong("date"); + slope = json.getDouble("slope"); + intercept = json.getDouble("intercept"); + scale = json.getDouble("scale"); + } catch (JSONException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/info/nightscout/client/data/NSProfile.java b/app/src/main/java/info/nightscout/client/data/NSProfile.java new file mode 100644 index 0000000000..30e0ba3594 --- /dev/null +++ b/app/src/main/java/info/nightscout/client/data/NSProfile.java @@ -0,0 +1,282 @@ +package info.nightscout.client.data; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.text.DecimalFormat; +import java.util.Calendar; +import java.util.TimeZone; + +public class NSProfile { + private JSONObject json = null; + private String activeProfile = null; + + public NSProfile(JSONObject json, String activeProfile) { + this.json = json; + this.activeProfile = activeProfile; + } + + JSONObject getDefaultProfile() { + String defaultProfileName = null; + JSONObject store; + JSONObject profile = null; + try { + defaultProfileName = (String) json.get("defaultProfile"); + store = json.getJSONObject("store"); + if (activeProfile != null && store.has(activeProfile)) { + defaultProfileName = activeProfile; + } + profile = store.getJSONObject(defaultProfileName); + } catch (JSONException e) { + e.printStackTrace(); + } + + return profile; + } + + public String log() { + String ret = "\n"; + for (Integer hour = 0; hour < 24; hour ++) { + double value = getBasal(hour * 60 * 60); + ret += "NS basal value for " + hour + ":00 is " + value + "\n"; + } + ret += "NS units: " + getUnits(); + return ret; + } + + public JSONObject getData () { + return json; + } + + public Double getDia() { + Double dia; + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + dia = profile.getDouble("dia"); + return dia; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 3D; + } + + public Double getCarbAbsorbtionRate() { + Double carbAbsorptionRate; + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + carbAbsorptionRate = profile.getDouble("carbs_hr"); + return carbAbsorptionRate; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + // mmol or mg/dl + public String getUnits() { + String units; + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + units = profile.getString("units"); + return units; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return "mg/dl"; + } + + public TimeZone getTimeZone() { + TimeZone timeZone; + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return TimeZone.getTimeZone(profile.getString("timezone")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return TimeZone.getDefault(); + } + + public Double getValueToTime(JSONArray array, Integer timeAsSeconds) { + Double lastValue = null; + + for(Integer index = 0; index < array.length(); index++) { + try { + JSONObject o = array.getJSONObject(index); + Integer tas = o.getInt("timeAsSeconds"); + Double value = o.getDouble("value"); + if (lastValue == null) lastValue = value; + if (timeAsSeconds < tas) { + break; + } + lastValue = value; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return lastValue; + } + + public String getValuesList(JSONArray array, JSONArray array2, DecimalFormat format, String units) { + String retValue = ""; + + for(Integer index = 0; index < array.length(); index++) { + try { + JSONObject o = array.getJSONObject(index); + retValue += o.getString("time"); + retValue += " "; + retValue += format.format(o.getDouble("value")); + if (array2 != null) { + JSONObject o2 = array2.getJSONObject(index); + retValue += " - "; + retValue += format.format(o2.getDouble("value")); + } + retValue += " " + units; + retValue += "\n"; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return retValue; + } + + public Double getIsf(Integer timeAsSeconds) { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValueToTime(profile.getJSONArray("sens"),timeAsSeconds); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + public String getIsfList() { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValuesList(profile.getJSONArray("sens"), null, new DecimalFormat("0"), getUnits() + "/U"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ""; + } + + public Double getIc(Integer timeAsSeconds) { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValueToTime(profile.getJSONArray("carbratio"),timeAsSeconds); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + public String getIcList() { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValuesList(profile.getJSONArray("carbratio"), null, new DecimalFormat("0"), "g"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ""; + } + + public Double getBasal(Integer timeAsSeconds) { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValueToTime(profile.getJSONArray("basal"),timeAsSeconds); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + public String getBasalList() { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValuesList(profile.getJSONArray("basal"), null, new DecimalFormat("0.00"), "U"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ""; + } + + public Double getTargetLow(Integer timeAsSeconds) { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValueToTime(profile.getJSONArray("target_low"),timeAsSeconds); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + public Double getTargetHigh(Integer timeAsSeconds) { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValueToTime(profile.getJSONArray("target_high"), timeAsSeconds); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return 0D; + } + + public String getTargetList() { + JSONObject profile = getDefaultProfile(); + if (profile != null) { + try { + return getValuesList(profile.getJSONArray("target_low"),profile.getJSONArray("target_high"), new DecimalFormat("0.0"), getUnits()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ""; + } + + public String getActiveProfile() { + return activeProfile; + } + + public Double getMaxDailyBasal() { + Double max = 0d; + for (Integer hour = 0; hour < 24; hour ++) { + double value = getBasal(hour * 60 * 60); + if (value > max) max = value; + } + return max; + } + + public static int secondsFromMidnight() { + Calendar c = Calendar.getInstance(); + long now = c.getTimeInMillis(); + 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/data/NSSgv.java b/app/src/main/java/info/nightscout/client/data/NSSgv.java new file mode 100644 index 0000000000..5ce9b35caa --- /dev/null +++ b/app/src/main/java/info/nightscout/client/data/NSSgv.java @@ -0,0 +1,63 @@ +package info.nightscout.client.data; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * + * {"mgdl":105,"mills":1455136282375,"device":"xDrip-BluetoothWixel","direction":"Flat","filtered":98272,"unfiltered":98272,"noise":1,"rssi":100} + */ +public class NSSgv { + private JSONObject data; + + public NSSgv(JSONObject obj) { + this.data = obj; + } + + private String getStringOrNull(String key) { + String ret = null; + if (data.has(key)) { + try { + ret = data.getString(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Integer getIntegerOrNull(String key) { + Integer ret = null; + if (data.has(key)) { + try { + ret = data.getInt(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Long getLongOrNull(String key) { + Long ret = null; + if (data.has(key)) { + try { + ret = data.getLong(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + public JSONObject getData () { return data; } + public Integer getMgdl () { return getIntegerOrNull("mgdl"); } + public Integer getFiltered () { return getIntegerOrNull("filtered"); } + public Integer getUnfiltered () { return getIntegerOrNull("unfiltered"); } + public Integer getNoise () { return getIntegerOrNull("noise"); } + public Integer getRssi () { return getIntegerOrNull("rssi"); } + public Long getMills () { return getLongOrNull("mills"); } + public String getDevice () { return getStringOrNull("device"); } + public String getDirection () { return getStringOrNull("direction"); } + +} diff --git a/app/src/main/java/info/nightscout/client/data/NSStatus.java b/app/src/main/java/info/nightscout/client/data/NSStatus.java new file mode 100644 index 0000000000..f247907296 --- /dev/null +++ b/app/src/main/java/info/nightscout/client/data/NSStatus.java @@ -0,0 +1,105 @@ +package info.nightscout.client.data; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Date; + +/** + { + status: 'ok' + , name: env.name + , version: env.version + , versionNum: versionNum (for ver 1.2.3 contains 10203) + , serverTime: new Date().toISOString() + , apiEnabled: apiEnabled + , careportalEnabled: apiEnabled && env.settings.enable.indexOf('careportal') > -1 + , boluscalcEnabled: apiEnabled && env.settings.enable.indexOf('boluscalc') > -1 + , head: env.head + , settings: env.settings + , extendedSettings: ctx.plugins && ctx.plugins.extendedClientSettings ? ctx.plugins.extendedClientSettings(env.extendedSettings) : {} + , activeProfile ..... calculated from treatments or missing + } + */ +public class NSStatus { + private JSONObject data; + + public NSStatus(JSONObject obj) { + this.data = obj; + } + + public String getName () { return getStringOrNull("name"); } + public String getVersion () { return getStringOrNull("version"); } + public Integer getVersionNum () { return getIntegerOrNull("versionNum"); } + public Date getServerTime () { return getDateOrNull("versionNum"); } + public boolean getApiEnabled () { return getBooleanOrNull("apiEnabled"); } + public boolean getCareportalEnabled () { return getBooleanOrNull("careportalEnabled"); } + public boolean getBoluscalcEnabled () { return getBooleanOrNull("boluscalcEnabled"); } + public String getHead () { return getStringOrNull("head"); } + public String getSettings () { return getStringOrNull("settings"); } + public String getExtendedSettings () { return getStringOrNull("extendedSettings"); } + public String getActiveProfile () { return getStringOrNull("activeProfile"); } + + private String getStringOrNull(String key) { + String ret = null; + if (data.has(key)) { + try { + ret = data.getString(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Integer getIntegerOrNull(String key) { + Integer ret = null; + if (data.has(key)) { + try { + ret = data.getInt(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Long getLongOrNull(String key) { + Long ret = null; + if (data.has(key)) { + try { + ret = data.getLong(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Date getDateOrNull(String key) { + Date ret = null; + if (data.has(key)) { + try { + ret = new Date(data.getString(key)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private boolean getBooleanOrNull(String key) { + boolean ret = false; + if (data.has(key)) { + try { + ret = data.getBoolean(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + public JSONObject getData () { return data; } + +} diff --git a/app/src/main/java/info/nightscout/client/data/NSTreatment.java b/app/src/main/java/info/nightscout/client/data/NSTreatment.java new file mode 100644 index 0000000000..f358b4e74d --- /dev/null +++ b/app/src/main/java/info/nightscout/client/data/NSTreatment.java @@ -0,0 +1,89 @@ +package info.nightscout.client.data; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Date; + +public class NSTreatment { + private JSONObject data; + private String action = null; // "update", "remove" or null (add) + + public NSTreatment(JSONObject obj) { + this.data = obj; + this.action = getStringOrNull("action"); + this.data.remove("action"); + } + + private String getStringOrNull(String key) { + String ret = null; + if (data.has(key)) { + try { + ret = data.getString(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Double getDoubleOrNull(String key) { + Double ret = null; + if (data.has(key)) { + try { + ret = data.getDouble(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Integer getIntegerOrNull(String key) { + Integer ret = null; + if (data.has(key)) { + try { + ret = data.getInt(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Long getLongOrNull(String key) { + Long ret = null; + if (data.has(key)) { + try { + ret = data.getLong(key); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + private Date getDateOrNull(String key) { + Date ret = null; + if (data.has(key)) { + try { + ret = new Date(data.getString(key)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return ret; + }; + + public String getAction() { return action; } + public JSONObject getData() { return data; } + public String get_id() { return getStringOrNull("_id"); } + public String getEnteredBy() { return getStringOrNull("enteredBy"); } + public String getEventType() { return getStringOrNull("eventType"); } + public Integer getHapp_id() { return getIntegerOrNull("happ_id"); } + public Integer getDuration() { return getIntegerOrNull("duration"); } + public Integer getMgdl() { return getIntegerOrNull("mgdl"); } + public Double getAbsolute() { return getDoubleOrNull("absolute"); } + public Long getMills() { return getLongOrNull("mills"); } + public Date getCreated_at() { return getDateOrNull("created_at"); } +} diff --git a/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java b/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java new file mode 100644 index 0000000000..3576e004cd --- /dev/null +++ b/app/src/main/java/info/nightscout/client/receivers/NSClientDataReceiver.java @@ -0,0 +1,258 @@ +package info.nightscout.client.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; + +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.stmt.Where; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.events.EventNewBasalProfile; +import info.nightscout.androidaps.events.EventTreatmentChange; +import info.nightscout.client.broadcasts.Intents; +import info.nightscout.client.data.NSProfile; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.db.Treatment; + +public class NSClientDataReceiver extends BroadcastReceiver { + private static Logger log = LoggerFactory.getLogger(NSClientDataReceiver.class); + public NSClientDataReceiver() { + } + + @Override + public void onReceive(Context context, Intent intent) { + Bundle bundles = intent.getExtras(); + if (bundles == null) return; + + + // Handle profile + if (intent.getAction().equals(Intents.ACTION_NEW_PROFILE)){ + try { + String activeProfile = bundles.getString("activeprofile"); + String profile = bundles.getString("profile"); + NSProfile nsProfile = new NSProfile(new JSONObject(profile), activeProfile); + MainApp.instance().setNSProfile(nsProfile); + MainApp.instance().setActiveProfile(activeProfile); + storeNSProfile(); + if (MainApp.getActivePump() != null) { + MainApp.getActivePump().setNewBasalProfile(); + } else { + log.error("No active pump selected"); + } + log.debug("Received profile: " + activeProfile + " " + profile); + MainApp.bus().post(new EventNewBasalProfile()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + if (intent.getAction().equals(Intents.ACTION_NEW_TREATMENT)) { + try { + String trstring = bundles.getString("treatment"); + JSONObject trJson = new JSONObject(trstring); + if (!trJson.has("insulin") && !trJson.has("carbs")) { + log.debug("ADD: Uninterested treatment: " + trstring); + return; + } + + Treatment stored = null; + trJson = new JSONObject(trstring); + String _id = trJson.getString("_id"); + + if (trJson.has("timeIndex")) { + log.debug("ADD: timeIndex found: " + trstring); + stored = findByTimeIndex(trJson.getLong("timeIndex")); + } else { + stored = findById(_id); + } + + if (stored != null) { + log.debug("ADD: Existing treatment: " + trstring); + if (trJson.has("timeIndex")) { + stored._id = _id; + MainApp.getDbHelper().getDaoTreatments().update(stored); + } + return; + } else { + log.debug("ADD: New treatment: " + trstring); + Treatment treatment = new Treatment(); + treatment._id = _id; + treatment.carbs = trJson.has("carbs") ? trJson.getDouble("carbs") : 0; + treatment.insulin = trJson.has("insulin") ? trJson.getDouble("insulin") : 0d; + treatment.created_at = new Date(trJson.getLong("mills")); + treatment.setTimeIndex(treatment.getTimeIndex()); + try { + MainApp.getDbHelper().getDaoTreatments().create(treatment); + log.debug("ADD: Stored treatment: " + treatment.log()); + MainApp.bus().post(new EventTreatmentChange()); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + } catch (Exception e1) { + e1.printStackTrace(); + } + + } + + if (intent.getAction().equals(Intents.ACTION_CHANGED_TREATMENT)) { + try { + String trstring = bundles.getString("treatment"); + JSONObject trJson = new JSONObject(trstring); + if (!trJson.has("insulin") && !trJson.has("carbs")) { + log.debug("CHANGE: Uninterested treatment: " + trstring); + return; + } + trJson = new JSONObject(trstring); + String _id = trJson.getString("_id"); + + Treatment stored; + + if (trJson.has("timeIndex")) { + log.debug("ADD: timeIndex found: " + trstring); + stored = findByTimeIndex(trJson.getLong("timeIndex")); + } else { + stored = findById(_id); + } + + if (stored != null) { + log.debug("CHANGE: Existing treatment: " + trstring); + stored._id = _id; + stored.carbs = trJson.has("carbs") ? trJson.getDouble("carbs") : 0; + stored.insulin = trJson.has("insulin") ? trJson.getDouble("insulin") : 0d; + stored.created_at = new Date(trJson.getLong("mills")); + MainApp.getDbHelper().getDaoTreatments().update(stored); + MainApp.bus().post(new EventTreatmentChange()); + } else { + log.debug("CHANGE: New treatment: " + trstring); + Treatment treatment = new Treatment(); + treatment._id = _id; + treatment.carbs = trJson.has("carbs") ? trJson.getDouble("carbs") : 0; + treatment.insulin = trJson.has("insulin") ? trJson.getDouble("insulin") : 0d; + //treatment.created_at = DateUtil.fromISODateString(trJson.getString("created_at")); + treatment.created_at = new Date(trJson.getLong("mills")); + treatment.setTimeIndex(treatment.getTimeIndex()); + try { + MainApp.getDbHelper().getDaoTreatments().create(treatment); + log.debug("CHANGE: Stored treatment: " + treatment.log()); + MainApp.bus().post(new EventTreatmentChange()); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + + if (intent.getAction().equals(Intents.ACTION_REMOVED_TREATMENT)) { + try { + if (bundles.containsKey("treatment")) { + String trstring = bundles.getString("treatment"); + JSONObject trJson = new JSONObject(trstring); + String _id = trJson.getString("_id"); + removeTreatmentFromDb(_id); + } + + if (bundles.containsKey("treatments")) { + String trstring = bundles.getString("treatments"); + JSONArray jsonArray = new JSONArray(trstring); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject trJson = jsonArray.getJSONObject(i); + String _id = trJson.getString("_id"); + removeTreatmentFromDb(_id); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + } + + public void storeNSProfile() { + SharedPreferences settings = MainApp.instance().getApplicationContext().getSharedPreferences(MainApp.instance().PREFS_NAME, 0); + SharedPreferences.Editor editor = settings.edit(); + editor.putString("profile", MainApp.instance().getNSProfile().getData().toString()); + editor.putString("activeProfile", MainApp.instance().getActiveProfile()); + editor.commit(); + } + + public static Treatment findById(String _id) { + try { + QueryBuilder qb = null; + Dao daoTreatments = MainApp.getDbHelper().getDaoTreatments(); + QueryBuilder queryBuilder = daoTreatments.queryBuilder(); + Where where = queryBuilder.where(); + where.eq("_id", _id); + queryBuilder.limit(10); + PreparedQuery preparedQuery = queryBuilder.prepare(); + List trList = daoTreatments.query(preparedQuery); + if (trList.size() != 1) { + //log.debug("Treatment findById query size: " + trList.size()); + return null; + } else { + //log.debug("Treatment findById found: " + trList.get(0).log()); + return trList.get(0); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public static Treatment findByTimeIndex(Long timeIndex) { + try { + QueryBuilder qb = null; + Dao daoTreatments = MainApp.getDbHelper().getDaoTreatments(); + QueryBuilder queryBuilder = daoTreatments.queryBuilder(); + Where where = queryBuilder.where(); + where.eq("timeIndex", timeIndex); + queryBuilder.limit(10); + PreparedQuery preparedQuery = queryBuilder.prepare(); + List trList = daoTreatments.query(preparedQuery); + if (trList.size() != 1) { + log.debug("Treatment findByTimeIndex query size: " + trList.size()); + return null; + } else { + log.debug("Treatment findByTimeIndex found: " + trList.get(0).log()); + return trList.get(0); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + private void removeTreatmentFromDb(String _id) throws SQLException { + Treatment stored = findById(_id); + if (stored != null) { + log.debug("REMOVE: Existing treatment (removing): " + _id); + MainApp.getDbHelper().getDaoTreatments().delete(stored); + MainApp.bus().post(new EventTreatmentChange()); + } else { + log.debug("REMOVE: Not stored treatment (ignoring): " + _id); + } + } +} diff --git a/app/src/main/java/info/nightscout/utils/DateUtil.java b/app/src/main/java/info/nightscout/utils/DateUtil.java new file mode 100644 index 0000000000..1b6f857c96 --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/DateUtil.java @@ -0,0 +1,52 @@ +package info.nightscout.utils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * The Class DateUtil. A simple wrapper around SimpleDateFormat to ease the handling of iso date string <-> date obj + * with TZ + */ +public class DateUtil +{ + + /** The date format in iso. */ + public static String FORMAT_DATE_ISO="yyyy-MM-dd'T'HH:mm:ssZ"; + + /** + * Takes in an ISO date string of the following format: + * yyyy-mm-ddThh:mm:ss.ms+HoMo + * + * @param isoDateString the iso date string + * @return the date + * @throws Exception the exception + */ + public static Date fromISODateString(String isoDateString) + throws Exception + { + DateFormat f = new SimpleDateFormat(FORMAT_DATE_ISO); + return f.parse(isoDateString); + } + + /** + * Render date + * + * @param date the date obj + * @param format - if not specified, will use FORMAT_DATE_ISO + * @param tz - tz to set to, if not specified uses local timezone + * @return the iso-formatted date string + */ + public static String toISOString(Date date, String format, TimeZone tz) + { + if( format == null ) format = FORMAT_DATE_ISO; + if( tz == null ) tz = TimeZone.getDefault(); + DateFormat f = new SimpleDateFormat(format); + f.setTimeZone(tz); + return f.format(date); + } + + public static String toISOString(Date date) + { return toISOString(date,FORMAT_DATE_ISO,TimeZone.getTimeZone("UTC")); } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_objectives.xml b/app/src/main/res/layout/objectives_fragment.xml similarity index 100% rename from app/src/main/res/layout/fragment_objectives.xml rename to app/src/main/res/layout/objectives_fragment.xml diff --git a/app/src/main/res/layout/objectives_item.xml b/app/src/main/res/layout/objectives_item.xml index aaf47894c9..2dea4f5fd0 100644 --- a/app/src/main/res/layout/objectives_item.xml +++ b/app/src/main/res/layout/objectives_item.xml @@ -3,7 +3,7 @@ xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/objectives_cardview" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_gravity="center" card_view:cardBackgroundColor="@color/cardColorBackground" card_view:cardCornerRadius="6dp" diff --git a/app/src/main/res/layout/profileviewer_fragment.xml b/app/src/main/res/layout/profileviewer_fragment.xml new file mode 100644 index 0000000000..4d3ac26bce --- /dev/null +++ b/app/src/main/res/layout/profileviewer_fragment.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/app_fragment.xml b/app/src/main/res/layout/test_fragment.xml similarity index 100% rename from app/src/main/res/layout/app_fragment.xml rename to app/src/main/res/layout/test_fragment.xml diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2e8e06ee58..1208fbb05f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,7 +3,7 @@ #3F51B5 #303F9F #FF4081 - #121212 + #121212 #779ECB diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8aae4883c4..12b1f8b0ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,5 +27,12 @@ Gate: Start Verify + DIA: + Active profile: + IC: + ISF: + Basal: + Target: + NO PROFILE SET From f7779bd9f1ada1f0de3e6a0e88d1f95a37a9bbef Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 5 Jun 2016 14:53:03 +0200 Subject: [PATCH 002/336] Treatments fragment --- app/src/main/AndroidManifest.xml | 6 +- .../nightscout/androidaps/MainActivity.java | 6 +- .../info/nightscout/androidaps/data/Iob.java | 15 ++ .../nightscout/androidaps/db/Treatment.java | 59 +++-- .../androidaps/events/EventNewBG.java | 7 + .../ProfileViewer/ProfileViewerFragment.java | 2 - .../Treatments/TreatmentsFragment.java | 237 ++++++++++++++++++ .../nightscout/client/data/NSProfile.java | 13 + .../receivers/NSClientDataReceiver.java | 6 + .../main/res/layout/treatments_fragment.xml | 58 +++++ app/src/main/res/layout/treatments_item.xml | 127 ++++++++++ app/src/main/res/values/strings.xml | 6 + 12 files changed, 508 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/data/Iob.java create mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java create mode 100644 app/src/main/res/layout/treatments_fragment.xml create mode 100644 app/src/main/res/layout/treatments_item.xml 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: From d6608bbcbcb97b2698eb03e423b647af4f967f94 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 5 Jun 2016 15:58:08 +0200 Subject: [PATCH 003/336] Update IOB on tab selection --- .../androidaps/plugins/Treatments/TreatmentsFragment.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index 23fbe175ad..ccf6b71599 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -220,6 +220,14 @@ public class TreatmentsFragment extends Fragment { recyclerView.getAdapter().notifyDataSetChanged(); } + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + + if (isVisibleToUser) + updateTotalIOB(); + } + /** * This interface must be implemented by activities that contain this * fragment to allow an interaction in this fragment to be communicated From 627ce66852b1fbbcec412608e7e108bc821f8b8d Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 6 Jun 2016 10:42:46 +0200 Subject: [PATCH 004/336] TempBasals & some IOB adjustments --- .../nightscout/androidaps/MainActivity.java | 5 +- .../info/nightscout/androidaps/data/Iob.java | 4 + .../androidaps/db/DatabaseHelper.java | 2 +- .../nightscout/androidaps/db/TempBasal.java | 143 ++++++--- .../nightscout/androidaps/db/Treatment.java | 9 +- .../events/EventTempBasalChange.java | 7 + .../TempBasals/TempBasalsFragment.java | 282 ++++++++++++++++++ .../Treatments/TreatmentsFragment.java | 1 - .../main/res/layout/tempbasals_fragment.xml | 58 ++++ app/src/main/res/layout/tempbasals_item.xml | 171 +++++++++++ app/src/main/res/values/strings.xml | 7 + 11 files changed, 642 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/TempBasals/TempBasalsFragment.java create mode 100644 app/src/main/res/layout/tempbasals_fragment.xml create mode 100644 app/src/main/res/layout/tempbasals_item.xml diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 8788020c87..83dbffab35 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import java.util.List; import info.nightscout.androidaps.plugins.ProfileViewer.ProfileViewerFragment; +import info.nightscout.androidaps.plugins.TempBasals.TempBasalsFragment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment; import info.nightscout.androidaps.tabs.*; import info.nightscout.androidaps.plugins.Objectives.ObjectivesFragment; @@ -23,7 +24,8 @@ import info.nightscout.client.broadcasts.Intents; public class MainActivity extends AppCompatActivity implements ObjectivesFragment.OnFragmentInteractionListener, - TreatmentsFragment.OnFragmentInteractionListener { + TreatmentsFragment.OnFragmentInteractionListener, + TempBasalsFragment.OnFragmentInteractionListener { private static Logger log = LoggerFactory.getLogger(MainActivity.class); private Toolbar toolbar; @@ -40,6 +42,7 @@ public class MainActivity extends AppCompatActivity mAdapter = new TabPageAdapter(getSupportFragmentManager()); mAdapter.registerNewFragment("Test", TestFragment.newInstance()); mAdapter.registerNewFragment("Treatments", TreatmentsFragment.newInstance()); + mAdapter.registerNewFragment("TempBasals", TempBasalsFragment.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 index ee70699604..92fdae4f7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Iob.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Iob.java @@ -6,10 +6,14 @@ package info.nightscout.androidaps.data; public class Iob { public double iobContrib = 0d; public double activityContrib = 0d; + public double netInsulin = 0d; // for calculations from temp basals only + public double netRatio = 0d; // for calculations from temp basals only public Iob plus(Iob iob) { iobContrib += iob.iobContrib; activityContrib += iob.activityContrib; + netInsulin += iob.netInsulin; + netRatio += iob.netRatio; return this; } } 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 3c9a4886a3..ed04522a71 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -19,7 +19,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public static final String DATABASE_NAME = "AndroidAPSDb"; - private static final int DATABASE_VERSION = 1; + private static final int DATABASE_VERSION = 2; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); diff --git a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java index 7ecd5b85e0..755e94e6fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java @@ -10,6 +10,10 @@ import java.util.Calendar; import java.util.Date; import java.util.TimeZone; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.Iob; +import info.nightscout.client.data.NSProfile; + @DatabaseTable(tableName = "TempBasals") public class TempBasal { private static Logger log = LoggerFactory.getLogger(TempBasal.class); @@ -33,10 +37,10 @@ public class TempBasal { public Date timeEnd; @DatabaseField - public int percent; // In % of current basal + public int percent; // In % of current basal. 100% == current basal @DatabaseField - public int absolute; // Absolute value in U + public Double absolute; // Absolute value in U @DatabaseField public int duration; // in minutes @@ -47,59 +51,107 @@ public class TempBasal { @DatabaseField public boolean isAbsolute; // true if if set as absolute value in U -/* - public Iob calcIob() { - Iob iob = new Iob(); - long msAgo = getMillisecondsFromStart(); - Calendar startAdjusted = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - startAdjusted.setTime(this.timeStart); - int minutes = startAdjusted.get(Calendar.MINUTE); - minutes = minutes % 4; - if (startAdjusted.get(Calendar.SECOND) > 0 && minutes == 0) { - minutes += 4; - } - startAdjusted.add(Calendar.MINUTE, minutes); - startAdjusted.set(Calendar.SECOND, 0); + public Iob iobCalc(Date time) { + Iob result = new Iob(); + NSProfile profile = MainApp.getNSProfile(); - IobCalc iobCalc = new IobCalc(); - iobCalc.setTime(new Date()); - iobCalc.setAmount(-1.0d * (baseRatio - tempRatio) / 15.0d / 100.0d); + if (profile == null) + return result; - long timeStartTime = startAdjusted.getTimeInMillis(); - Date currentTimeEnd = timeEnd; - if (currentTimeEnd == null) { - currentTimeEnd = new Date(); - if (getPlannedTimeEnd().getTime() < currentTimeEnd.getTime()) { - currentTimeEnd = getPlannedTimeEnd(); + int realDuration = getRealDuration(); + + if (realDuration > 0) { + Double netBasalRate = 0d; + Double basalRate = profile.getBasal(profile.secondsFromMidnight(time)); + Double tempBolusSize = 0.05; + + if (this.isAbsolute) { + netBasalRate = this.absolute - basalRate; + } else { + netBasalRate = (this.percent - 100) / 100d * basalRate; + } + + result.netRatio = netBasalRate; + Double netBasalAmount = Math.round(netBasalRate * realDuration * 10 / 6) / 100d; + result.netInsulin = netBasalAmount; + if (netBasalAmount < 0.1) { + tempBolusSize = 0.01; + } + if (netBasalRate < 0) { + tempBolusSize = -tempBolusSize; + } + Long tempBolusCount = Math.round(netBasalAmount / tempBolusSize); + if (tempBolusCount > 0) { + Long tempBolusSpacing = realDuration / tempBolusCount; + for (Long j = 0l; j < tempBolusCount; j++) { + Treatment tempBolusPart = new Treatment(); + tempBolusPart.insulin = tempBolusSize; + Long date = this.timeStart.getTime() + j * tempBolusSpacing * 60 * 1000; + tempBolusPart.created_at = new Date(date); + Iob iob = tempBolusPart.iobCalc(time); + result.plus(iob); + } } } - for (long time = timeStartTime; time < currentTimeEnd.getTime(); time += 4 * 60_000) { - Date start = new Date(time); - - iobCalc.setTimeStart(start); - iob.plus(iobCalc.invoke()); - } - - if (Settings.logTempIOBCalculation) { - log.debug("TempIOB start: " + this.timeStart + " end: " + this.timeEnd + " Percent: " + this.percent + " Duration: " + this.duration + " CalcDurat: " + (int) ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) - + "min minAgo: " + (int) (msAgo / 1000 / 60) + " IOB: " + iob.iobContrib + " Activity: " + iob.activityContrib + " Impact: " + (-0.01d * (baseRatio - tempRatio) * ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) / 60) - ); - } - - return iob; + return result; } -*/ + + /* + public Iob calcIob() { + Iob iob = new Iob(); + + long msAgo = getMillisecondsFromStart(); + Calendar startAdjusted = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + startAdjusted.setTime(this.timeStart); + int minutes = startAdjusted.get(Calendar.MINUTE); + minutes = minutes % 4; + if (startAdjusted.get(Calendar.SECOND) > 0 && minutes == 0) { + minutes += 4; + } + startAdjusted.add(Calendar.MINUTE, minutes); + startAdjusted.set(Calendar.SECOND, 0); + + IobCalc iobCalc = new IobCalc(); + iobCalc.setTime(new Date()); + iobCalc.setAmount(-1.0d * (baseRatio - tempRatio) / 15.0d / 100.0d); + + long timeStartTime = startAdjusted.getTimeInMillis(); + Date currentTimeEnd = timeEnd; + if (currentTimeEnd == null) { + currentTimeEnd = new Date(); + if (getPlannedTimeEnd().getTime() < currentTimeEnd.getTime()) { + currentTimeEnd = getPlannedTimeEnd(); + } + } + for (long time = timeStartTime; time < currentTimeEnd.getTime(); time += 4 * 60_000) { + Date start = new Date(time); + + iobCalc.setTimeStart(start); + iob.plus(iobCalc.invoke()); + } + + if (Settings.logTempIOBCalculation) { + log.debug("TempIOB start: " + this.timeStart + " end: " + this.timeEnd + " Percent: " + this.percent + " Duration: " + this.duration + " CalcDurat: " + (int) ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) + + "min minAgo: " + (int) (msAgo / 1000 / 60) + " IOB: " + iob.iobContrib + " Activity: " + iob.activityContrib + " Impact: " + (-0.01d * (baseRatio - tempRatio) * ((currentTimeEnd.getTime() - this.timeStart.getTime()) / 1000 / 60) / 60) + ); + } + + return iob; + } + */ // Determine end of basal public Date getTimeEnd() { Date tempBasalTimePlannedEnd = getPlannedTimeEnd(); + Date now = new Date(); - // End already exists in database - if (timeEnd != null) { - return timeEnd; + if (timeEnd != null && timeEnd.getTime() < tempBasalTimePlannedEnd.getTime()) { + tempBasalTimePlannedEnd = timeEnd; } - // if not return planned time + if (now.getTime() < tempBasalTimePlannedEnd.getTime()) + tempBasalTimePlannedEnd = now; + return tempBasalTimePlannedEnd; } @@ -107,6 +159,11 @@ public class TempBasal { return new Date(timeStart.getTime() + 60 * 1_000 * duration); } + public int getRealDuration() { + Long msecs = getTimeEnd().getTime() - timeStart.getTime(); + return (int) (msecs / 60 / 1000); + } + public long getMillisecondsFromStart() { return new Date().getTime() - timeStart.getTime(); } 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 b09714ab62..132e87bf14 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -57,6 +57,13 @@ public class Treatment { } public Iob iobCalc(Date time) { + Iob resultNow = doIobCalc(time); + Iob resultIn5min = doIobCalc(new Date(time.getTime() + 5 * 60 * 1000)); + resultNow.activityContrib = resultNow.iobContrib - resultIn5min.iobContrib; + return resultNow; + } + + public Iob doIobCalc(Date time) { Iob result = new Iob(); NSProfile profile = MainApp.getNSProfile(); @@ -77,7 +84,7 @@ public class Treatment { Double minAgo = scaleFactor * (time.getTime() - bolusTime) / 1000d / 60d; if (minAgo < peak) { - Double x1 = minAgo / 5 + 1; + Double x1 = minAgo / 5d + 1; result.iobContrib = this.insulin * (1 - 0.001852 * x1 * x1 + 0.001852 * x1); // units: BG (mg/dL) = (BG/U) * U insulin * scalar result.activityContrib = sens * this.insulin * (2 / dia / 60 / peak) * minAgo; diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java new file mode 100644 index 0000000000..5d8f961e75 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 05.06.2016. + */ +public class EventTempBasalChange { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/TempBasals/TempBasalsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/TempBasals/TempBasalsFragment.java new file mode 100644 index 0000000000..e9846e149f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/TempBasals/TempBasalsFragment.java @@ -0,0 +1,282 @@ +package info.nightscout.androidaps.plugins.TempBasals; + +import android.content.Context; +import android.net.Uri; +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.TempBasal; +import info.nightscout.androidaps.events.EventNewBG; +import info.nightscout.androidaps.events.EventTempBasalChange; + + +public class TempBasalsFragment extends Fragment { + private static Logger log = LoggerFactory.getLogger(TempBasalsFragment.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 tempBasals; + + private void initializeData() { + try { + Dao dao = MainApp.getDbHelper().getDaoTempBasals(); + + // **************** TESTING CREATE FAKE RECORD ***************** + TempBasal fake = new TempBasal(); + fake.timeStart = new Date(new Date().getTime() - 45 * 40 * 1000); + fake.timeEnd = new Date(new Date().getTime() - new Double(Math.random() * 45d * 40 * 1000).longValue()); + fake.duration = 30; + fake.percent = 150; + fake.isAbsolute = false; + fake.isExtended = false; + dao.create(fake); + // **************** TESTING CREATE FAKE RECORD ***************** + + QueryBuilder queryBuilder = dao.queryBuilder(); + queryBuilder.orderBy("timeIndex", false); + queryBuilder.limit(30l); + PreparedQuery preparedQuery = queryBuilder.prepare(); + tempBasals = dao.query(preparedQuery); + } catch (SQLException e) { + log.debug(e.getMessage(), e); + tempBasals = new ArrayList(); + } + if (recyclerView != null) { + recyclerView.swapAdapter(new RecyclerViewAdapter(tempBasals), false); + } + + + + updateTotalIOB(); + } + + private void updateTotalIOB() { + Iob total = new Iob(); + for (Integer pos = 0; pos < tempBasals.size(); pos++) { + TempBasal t = tempBasals.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 tempBasals; + + RecyclerViewAdapter(List tempBasals) { + this.tempBasals = tempBasals; + } + + @Override + public TempBasalsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.tempbasals_item, viewGroup, false); + TempBasalsViewHolder tempBasalsViewHolder = new TempBasalsViewHolder(v); + return tempBasalsViewHolder; + } + + @Override + public void onBindViewHolder(TempBasalsViewHolder holder, int position) { + // TODO: implement locales + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, new Locale("cs", "CZ")); + DateFormat enddf = DateFormat.getTimeInstance(DateFormat.SHORT, new Locale("cs", "CZ")); + if (tempBasals.get(position).timeEnd != null) { + holder.date.setText(df.format(tempBasals.get(position).timeStart) + " - " + enddf.format(tempBasals.get(position).timeEnd)); + } else { + holder.date.setText(df.format(tempBasals.get(position).timeStart)); + } + holder.duration.setText(formatNumber0decimalplaces.format(tempBasals.get(position).duration) + " min"); + if (tempBasals.get(position).isAbsolute) { + holder.absolute.setText(formatNumber0decimalplaces.format(tempBasals.get(position).absolute) + " U/h"); + holder.percent.setText(""); + } else { + holder.absolute.setText(""); + holder.percent.setText(formatNumber0decimalplaces.format(tempBasals.get(position).percent) + "%"); + } + holder.realDuration.setText(formatNumber0decimalplaces.format(tempBasals.get(position).getRealDuration()) + " min"); + Iob iob = tempBasals.get(position).iobCalc(new Date()); + holder.iob.setText(formatNumber2decimalplaces.format(iob.iobContrib) + " U"); + holder.activity.setText(formatNumber3decimalplaces.format(iob.activityContrib) + " U"); + holder.netInsulin.setText(formatNumber2decimalplaces.format(iob.netInsulin) + " U"); + holder.netRatio.setText(formatNumber2decimalplaces.format(iob.netRatio) + " U/h"); + } + + @Override + public int getItemCount() { + return tempBasals.size(); + } + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + } + + public static class TempBasalsViewHolder extends RecyclerView.ViewHolder { + CardView cv; + TextView date; + TextView duration; + TextView absolute; + TextView percent; + TextView realDuration; + TextView netRatio; + TextView netInsulin; + TextView iob; + TextView activity; + + TempBasalsViewHolder(View itemView) { + super(itemView); + cv = (CardView) itemView.findViewById(R.id.tempbasals_cardview); + date = (TextView) itemView.findViewById(R.id.tempbasals_date); + duration = (TextView) itemView.findViewById(R.id.tempbasals_duration); + absolute = (TextView) itemView.findViewById(R.id.tempbasals_absolute); + percent = (TextView) itemView.findViewById(R.id.tempbasals_percent); + realDuration = (TextView) itemView.findViewById(R.id.tempbasals_realduration); + netRatio = (TextView) itemView.findViewById(R.id.tempbasals_netratio); + netInsulin = (TextView) itemView.findViewById(R.id.tempbasals_netinsulin); + iob = (TextView) itemView.findViewById(R.id.tempbasals_iob); + activity = (TextView) itemView.findViewById(R.id.tempbasals_activity); + } + } + } + + public TempBasalsFragment() { + super(); + initializeData(); + } + + public static TempBasalsFragment newInstance() { + TempBasalsFragment fragment = new TempBasalsFragment(); + 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.tempbasals_fragment, container, false); + + recyclerView = (RecyclerView) view.findViewById(R.id.tempbasals_recyclerview); + recyclerView.setHasFixedSize(true); + llm = new LinearLayoutManager(view.getContext()); + recyclerView.setLayoutManager(llm); + + RecyclerViewAdapter adapter = new RecyclerViewAdapter(tempBasals); + recyclerView.setAdapter(adapter); + + iobTotal = (TextView) view.findViewById(R.id.tempbasals_iobtotal); + activityTotal = (TextView) view.findViewById(R.id.tempbasals_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 EventTempBasalChange ev) { + initializeData(); + } + + @Subscribe + public void onStatusEvent(final EventNewBG ev) { + updateTotalIOB(); + recyclerView.getAdapter().notifyDataSetChanged(); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + + if (isVisibleToUser) + updateTotalIOB(); + } + + /** + * 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/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index ccf6b71599..6fb32c044d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -66,7 +66,6 @@ public class TreatmentsFragment extends Fragment { } if (recyclerView != null) { recyclerView.swapAdapter(new RecyclerViewAdapter(treatments), false); - //recyclerView.getAdapter().notifyDataSetChanged(); } updateTotalIOB(); } diff --git a/app/src/main/res/layout/tempbasals_fragment.xml b/app/src/main/res/layout/tempbasals_fragment.xml new file mode 100644 index 0000000000..404f4ed404 --- /dev/null +++ b/app/src/main/res/layout/tempbasals_fragment.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/tempbasals_item.xml b/app/src/main/res/layout/tempbasals_item.xml new file mode 100644 index 0000000000..5b16dd3caf --- /dev/null +++ b/app/src/main/res/layout/tempbasals_item.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e866eff2f..5ddc0ed834 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,5 +40,12 @@ Activity: Toal IOB: Total IOB activity: + Dur: + Ratio: + Ins: + IOB: + Activity: + Total IOB: + Total IOB activity: From 21a34344633720fd88a7d06c94217e603407d7b2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 6 Jun 2016 11:24:17 +0200 Subject: [PATCH 005/336] fix activity contrib --- .../main/java/info/nightscout/androidaps/db/Treatment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 132e87bf14..807ca23f92 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -87,12 +87,12 @@ public class Treatment { Double x1 = minAgo / 5d + 1; result.iobContrib = this.insulin * (1 - 0.001852 * x1 * x1 + 0.001852 * x1); // units: BG (mg/dL) = (BG/U) * U insulin * scalar - result.activityContrib = sens * this.insulin * (2 / dia / 60 / peak) * minAgo; + result.activityContrib = this.insulin * (2 / dia / 60 / peak) * minAgo; } else if (minAgo < end) { Double x2 = (minAgo - 75) / 5; result.iobContrib = this.insulin * (0.001323 * x2 * x2 - 0.054233 * x2 + 0.55556); - result.activityContrib = sens * this.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * dia - peak)); + result.activityContrib = this.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * dia - peak)); } } return result; From bd47fb86243761263d75c1107c65dfe4761e19ef Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 6 Jun 2016 12:17:37 +0200 Subject: [PATCH 006/336] button refresh treatments from nightscout --- .../Treatments/TreatmentsFragment.java | 27 +++++++++++++------ .../nightscout/client/broadcasts/Intents.java | 1 + .../main/res/layout/treatments_fragment.xml | 7 +++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index 6fb32c044d..79489c46ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -1,6 +1,7 @@ package info.nightscout.androidaps.plugins.Treatments; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; @@ -9,6 +10,7 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.TextView; import com.j256.ormlite.dao.Dao; @@ -34,8 +36,9 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.events.EventTreatmentChange; +import info.nightscout.client.broadcasts.Intents; -public class TreatmentsFragment extends Fragment { +public class TreatmentsFragment extends Fragment implements View.OnClickListener { private static Logger log = LoggerFactory.getLogger(TreatmentsFragment.class); RecyclerView recyclerView; @@ -43,6 +46,7 @@ public class TreatmentsFragment extends Fragment { TextView iobTotal; TextView activityTotal; + Button refreshFromNS; private static DecimalFormat formatNumber0decimalplaces = new DecimalFormat("0"); private static DecimalFormat formatNumber2decimalplaces = new DecimalFormat("0.00"); @@ -171,17 +175,24 @@ public class TreatmentsFragment extends Fragment { iobTotal = (TextView) view.findViewById(R.id.treatments_iobtotal); activityTotal = (TextView) view.findViewById(R.id.treatments_iobactivitytotal); + refreshFromNS = (Button) view.findViewById(R.id.treatments_reshreshfromnightscout); + + refreshFromNS.setOnClickListener(this); 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 onClick(View view) { + switch (view.getId()) { + case R.id.treatments_reshreshfromnightscout: + MainApp.getDbHelper().resetTreatments(); + initializeData(); + Intent restartNSClient = new Intent(Intents.ACTION_RESTART); + MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + break; } - */ + } + @Override public void onAttach(Context context) { super.onAttach(context); diff --git a/app/src/main/java/info/nightscout/client/broadcasts/Intents.java b/app/src/main/java/info/nightscout/client/broadcasts/Intents.java index f667b048ed..8d921d879a 100644 --- a/app/src/main/java/info/nightscout/client/broadcasts/Intents.java +++ b/app/src/main/java/info/nightscout/client/broadcasts/Intents.java @@ -12,4 +12,5 @@ public interface Intents { // App -> NSClient String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; + String ACTION_RESTART = "info.nightscout.client.RESTART"; } diff --git a/app/src/main/res/layout/treatments_fragment.xml b/app/src/main/res/layout/treatments_fragment.xml index 578e1b3900..1085adbc36 100644 --- a/app/src/main/res/layout/treatments_fragment.xml +++ b/app/src/main/res/layout/treatments_fragment.xml @@ -47,6 +47,13 @@ android:paddingLeft="10dp" /> +