From 6fcb1ce23025c73a427cbfaa07c5db5677dd6dec Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Sun, 10 Mar 2019 19:08:54 +0000 Subject: [PATCH] Medtronic 0.8.1-SNAPSHOT - wear changes - refactoring of decoder - started work on bolus, tbr, resumes --- app/build.gradle | 17 +- app/src/main/AndroidManifest.xml | 58 ++- .../androidaps/db/DatabaseHelper.java | 281 +++++----- .../interfaces/TreatmentsInterface.java | 51 +- .../general/overview/OverviewFragment.java | 4 +- .../plugins/general/wear/WearPlugin.java | 71 ++- .../SendToDataLayerThread.java | 130 ++++- .../wearintegration/WatchUpdaterService.java | 295 ++++++++--- .../pump/medtronic/MedtronicPumpPlugin.java | 83 +-- .../comm/MedtronicCommunicationManager.java | 4 +- .../comm/history/MedtronicHistoryDecoder.java | 32 +- .../MedtronicHistoryDecoderInterface.java | 16 + .../cgms/MedtronicCGMSHistoryDecoder.java | 25 +- .../pump/MedtronicPumpHistoryDecoder.java | 35 +- .../comm/ui/MedtronicUIPostprocessor.java | 2 +- .../medtronic/data/MedtronicHistoryData.java | 231 +++++---- .../pump/medtronic/data/dto/ClockDTO.java | 4 +- .../plugins/treatments/TreatmentsPlugin.java | 168 ++++-- app/src/main/res/values/wear.xml | 18 + app/src/test/java/android/util/Log.java | 59 +++ .../comm/MedtronicHistoryDataUTest.java | 75 +++ .../MedtronicPumpHistoryDecoderUTest.java | 5 +- .../medtronic/data/dto/BasalProfileUTest.java | 10 +- .../androidaps/utils/LoggerRule.java | 62 +++ wear/build.gradle | 5 +- wear/src/main/AndroidManifest.xml | 61 ++- .../androidaps/data/ListenerService.java | 482 ++++++++++++++---- .../interaction/utils/WearUtil.java | 17 + wear/src/main/res/values/wear.xml | 18 + 29 files changed, 1665 insertions(+), 654 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java create mode 100644 app/src/main/res/values/wear.xml create mode 100644 app/src/test/java/android/util/Log.java create mode 100644 app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicHistoryDataUTest.java create mode 100644 app/src/test/java/info/nightscout/androidaps/utils/LoggerRule.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java create mode 100644 wear/src/main/res/values/wear.xml diff --git a/app/build.gradle b/app/build.gradle index c06bb0f6dd..812338b963 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,7 +64,7 @@ android { multiDexEnabled true versionCode 1500 // dev_version: 2.1 - version "medtronic-0.8.0" + version "medtronic-0.8.1-SNAPSHOT" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"' @@ -72,6 +72,7 @@ android { buildConfigField "String", "DEV_DATE", '"2.3.2019"' buildConfigField "String", "DEV_CHECKIN", '"f28d763ada9e6a8d7621b972e03edd980767e076"' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + // if you change minSdkVersion to less than 11, you need to change executeTask for wear ndk { moduleName "BleCommandUtil" @@ -81,7 +82,7 @@ android { // TODO remove once wear dependency com.google.android.gms:play-services-wearable:7.3.0 // has been upgraded (requiring significant code changes), which currently fails release // build with a deprecation warning - abortOnError false + // abortOnError false // (disabled entirely to avoid reports on the error, which would still be displayed // and it's easy to overlook that it's ignored) checkReleaseBuilds false @@ -103,9 +104,9 @@ android { applicationId "info.nightscout.androidaps" dimension "standard" resValue "string", "app_name", "AndroidAPS" - versionName version + "-pumpcontrol" + versionName version manifestPlaceholders = [ - appIcon: "@mipmap/ic_launcher", + appIcon : "@mipmap/ic_launcher", appIconRound: "@mipmap/ic_launcher_round" ] } @@ -125,7 +126,7 @@ android { resValue "string", "app_name", "NSClient" versionName version + "-nsclient" manifestPlaceholders = [ - appIcon: "@mipmap/ic_yellowowl", + appIcon : "@mipmap/yellowowl", appIconRound: "@null" ] } @@ -135,7 +136,7 @@ android { resValue "string", "app_name", "NSClient2" versionName version + "-nsclient" manifestPlaceholders = [ - appIcon: "@mipmap/ic_yellowowl", + appIcon : "@mipmap/yellowowl", appIconRound: "@null" ] } @@ -198,7 +199,7 @@ dependencies { implementation "org.slf4j:slf4j-api:1.7.12" implementation "com.jjoe64:graphview:4.0.1" implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1" - implementation "com.google.android.gms:play-services-wearable:7.5.0" + implementation 'com.google.android.gms:play-services-wearable:10.2.1' implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar") implementation(name: "sightparser-release", ext: "aar") implementation 'com.madgag.spongycastle:core:1.58.0.0' @@ -232,6 +233,8 @@ dependencies { testImplementation "com.google.truth:truth:0.39" testImplementation 'org.robolectric:robolectric:3.8' testImplementation "org.skyscreamer:jsonassert:1.5.0" + testImplementation "org.hamcrest:hamcrest-all:1.3" + testImplementation "uk.org.lidalia:slf4j-test:1.2.0" androidTestImplementation "org.mockito:mockito-core:2.7.22" androidTestImplementation "com.google.dexmaker:dexmaker:${dexmakerVersion}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1f64b2daa6..61d3f6c1b3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -174,7 +174,63 @@ android:name=".plugins.general.wear.wearintegration.WatchUpdaterService" android:exported="true"> - + + + + + + + + + + + + + + + + + + - * This class can safely be called from Services, but should not call Services to avoid circular dependencies. - * One major issue with this (right now) are the scheduled events, which are put into the service. Therefor all - * direct calls to the corresponding methods (eg. resetDatabases) should be done by a central service. + * This class can safely be called from Services, but should not call Services to avoid circular dependencies. One major + * issue with this (right now) are the scheduled events, which are put into the service. Therefor all direct calls to + * the corresponding methods (eg. resetDatabases) should be done by a central service. */ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @@ -106,7 +107,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final ScheduledExecutorService careportalEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledCareportalEventPost = null; - private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor(); + private static final ScheduledExecutorService profileSwitchEventWorker = Executors + .newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledProfileSwitchEventPost = null; private int oldVersion = 0; @@ -116,7 +118,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); onCreate(getWritableDatabase(), getConnectionSource()); - //onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1); + // onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1); } @@ -241,15 +243,13 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { scheduleTemporaryTargetChange(); scheduleCareportalEventChange(); scheduleProfileSwitchChange(); - new java.util.Timer().schedule( - new java.util.TimerTask() { - @Override - public void run() { - MainApp.bus().post(new EventRefreshOverview("resetDatabases")); - } - }, - 3000 - ); + new java.util.Timer().schedule(new java.util.TimerTask() { + + @Override + public void run() { + MainApp.bus().post(new EventRefreshOverview("resetDatabases")); + } + }, 3000); } @@ -367,18 +367,22 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return getDao(ProfileSwitch.class); } + private Dao getDaoInsightPumpID() throws SQLException { return getDao(InsightPumpID.class); } + private Dao getDaoInsightBolusID() throws SQLException { return getDao(InsightBolusID.class); } + private Dao getDaoInsightHistoryOffset() throws SQLException { return getDao(InsightHistoryOffset.class); } + public static long roundDateToSec(long date) { long rounded = date - date % 1000; if (rounded != date) @@ -434,6 +438,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventNewBg"); + Log.d("DatabaseHelper", "WR: Firing EventNewBg"); MainApp.bus().post(new EventNewBG(bgReading)); scheduledBgPost = null; } @@ -703,7 +708,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(tempTarget); getDaoTempTargets().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPTARGET: Updating record by date from: " + Source.getString(tempTarget.source) + " " + old.toString()); + log.debug("TEMPTARGET: Updating record by date from: " + + Source.getString(tempTarget.source) + " " + old.toString()); scheduleTemporaryTargetChange(); return true; } @@ -723,7 +729,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(tempTarget); getDaoTempTargets().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPTARGET: Updating record by _id from: " + Source.getString(tempTarget.source) + " " + old.toString()); + log.debug("TEMPTARGET: Updating record by _id from: " + + Source.getString(tempTarget.source) + " " + old.toString()); scheduleTemporaryTargetChange(); return true; } @@ -731,14 +738,16 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } getDaoTempTargets().create(tempTarget); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + tempTarget.toString()); + log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + + tempTarget.toString()); scheduleTemporaryTargetChange(); return true; } if (tempTarget.source == Source.USER) { getDaoTempTargets().create(tempTarget); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + tempTarget.toString()); + log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + + tempTarget.toString()); scheduleTemporaryTargetChange(); return true; } @@ -798,14 +807,11 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void createTemptargetFromJsonIfNotExists(JSONObject trJson) { try { String units = JsonHelper.safeGetString(trJson, "units", Constants.MGDL); - TempTarget tempTarget = new TempTarget() - .date(trJson.getLong("mills")) - .duration(trJson.getInt("duration")) - .low(Profile.toMgdl(trJson.getDouble("targetBottom"), units)) - .high(Profile.toMgdl(trJson.getDouble("targetTop"), units)) - .reason(JsonHelper.safeGetString(trJson, "reason", "")) - ._id(trJson.getString("_id")) - .source(Source.NIGHTSCOUT); + TempTarget tempTarget = new TempTarget().date(trJson.getLong("mills")).duration(trJson.getInt("duration")) + .low(Profile.toMgdl(trJson.getDouble("targetBottom"), units)) + .high(Profile.toMgdl(trJson.getDouble("targetTop"), units)) + .reason(JsonHelper.safeGetString(trJson, "reason", ""))._id(trJson.getString("_id")) + .source(Source.NIGHTSCOUT); createOrUpdate(tempTarget); } catch (JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); @@ -849,7 +855,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { try { getDaoDanaRHistory().createOrUpdate(record); - //If it is a TDD, store it for stats also. + // If it is a TDD, store it for stats also. if (record.recordCode == RecordTypes.RECORD_TYPE_DAILY) { createOrUpdateTDD(new TDD(record.recordDate, record.recordDailyBolus, record.recordDailyBasal, 0)); } @@ -906,7 +912,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // ------------ TemporaryBasal handling --------------- - //return true if new record was created + // return true if new record was created public boolean createOrUpdate(TemporaryBasal tempBasal) { try { TemporaryBasal old; @@ -922,12 +928,14 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (trList.size() > 0) { // do nothing, pump history record cannot be changed if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); + log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + + tempBasal.toString()); return false; } getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); + log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; @@ -945,7 +953,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(tempBasal); getDaoTemporaryBasal().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: Updating record by date from: " + Source.getString(tempBasal.source) + " " + old.toString()); + log.debug("TEMPBASAL: Updating record by date from: " + Source.getString(tempBasal.source) + + " " + old.toString()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleTemporaryBasalChange(); @@ -968,7 +977,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(tempBasal); getDaoTemporaryBasal().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: Updating record by _id from: " + Source.getString(tempBasal.source) + " " + old.toString()); + log.debug("TEMPBASAL: Updating record by _id from: " + + Source.getString(tempBasal.source) + " " + old.toString()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleTemporaryBasalChange(); @@ -978,7 +988,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); + log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; @@ -986,7 +997,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (tempBasal.source == Source.USER) { getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) - log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); + log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; @@ -1052,31 +1064,28 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { /* - { - "_id": "59232e1ddd032d04218dab00", - "eventType": "Temp Basal", - "duration": 60, - "percent": -50, - "created_at": "2017-05-22T18:29:57Z", - "enteredBy": "AndroidAPS", - "notes": "Basal Temp Start 50% 60.0 min", - "NSCLIENT_ID": 1495477797863, - "mills": 1495477797000, - "mgdl": 194.5, - "endmills": 1495481397000 - } - */ + * { + * "_id": "59232e1ddd032d04218dab00", + * "eventType": "Temp Basal", + * "duration": 60, + * "percent": -50, + * "created_at": "2017-05-22T18:29:57Z", + * "enteredBy": "AndroidAPS", + * "notes": "Basal Temp Start 50% 60.0 min", + * "NSCLIENT_ID": 1495477797863, + * "mills": 1495477797000, + * "mgdl": 194.5, + * "endmills": 1495481397000 + * } + */ public void createTempBasalFromJsonIfNotExists(JSONObject trJson) { try { if (trJson.has("originalExtendedAmount")) { // extended bolus uploaded as temp basal - ExtendedBolus extendedBolus = new ExtendedBolus() - .source(Source.NIGHTSCOUT) - .date(trJson.getLong("mills")) - .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0) - .durationInMinutes(trJson.getInt("duration")) - .insulin(trJson.getDouble("originalExtendedAmount")) - ._id(trJson.getString("_id")); + ExtendedBolus extendedBolus = new ExtendedBolus().source(Source.NIGHTSCOUT) + .date(trJson.getLong("mills")).pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0) + .durationInMinutes(trJson.getInt("duration")).insulin(trJson.getDouble("originalExtendedAmount")) + ._id(trJson.getString("_id")); // if faking found in NS, adapt AAPS to use it too if (!VirtualPumpPlugin.getPlugin().getFakingStatus()) { VirtualPumpPlugin.getPlugin().setFakingStatus(true); @@ -1100,10 +1109,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } createOrUpdate(extendedBolus); } else { - TemporaryBasal tempBasal = new TemporaryBasal() - .date(trJson.getLong("mills")) - .source(Source.NIGHTSCOUT) - .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0); + TemporaryBasal tempBasal = new TemporaryBasal().date(trJson.getLong("mills")).source(Source.NIGHTSCOUT) + .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0); if (trJson.has("duration")) { tempBasal.durationInMinutes = trJson.getInt("duration"); } @@ -1162,7 +1169,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public boolean createOrUpdate(ExtendedBolus extendedBolus) { try { if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: createOrUpdate: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); + log.debug("EXTENDEDBOLUS: createOrUpdate: " + Source.getString(extendedBolus.source) + " " + + extendedBolus.log()); ExtendedBolus old; extendedBolus.date = roundDateToSec(extendedBolus.date); @@ -1187,7 +1195,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { getDaoExtendedBolus().createOrUpdate(extendedBolus); } if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); + log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; @@ -1201,7 +1210,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(extendedBolus); getDaoExtendedBolus().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: Updating record by date from: " + Source.getString(extendedBolus.source) + " " + old.log()); + log.debug("EXTENDEDBOLUS: Updating record by date from: " + + Source.getString(extendedBolus.source) + " " + old.log()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleExtendedBolusChange(); @@ -1224,7 +1234,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(extendedBolus); getDaoExtendedBolus().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: Updating record by _id from: " + Source.getString(extendedBolus.source) + " " + old.log()); + log.debug("EXTENDEDBOLUS: Updating record by _id from: " + + Source.getString(extendedBolus.source) + " " + old.log()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleExtendedBolusChange(); @@ -1234,7 +1245,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } getDaoExtendedBolus().create(extendedBolus); if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); + log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; @@ -1242,7 +1254,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (extendedBolus.source == Source.USER) { getDaoExtendedBolus().create(extendedBolus); if (L.isEnabled(L.DATABASE)) - log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); + log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; @@ -1253,17 +1266,17 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return false; } + public ExtendedBolus getExtendedBolusByPumpId(long pumpId) { try { - return getDaoExtendedBolus().queryBuilder() - .where().eq("pumpId", pumpId) - .queryForFirst(); + return getDaoExtendedBolus().queryBuilder().where().eq("pumpId", pumpId).queryForFirst(); } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } + public void delete(ExtendedBolus extendedBolus) { try { getDaoExtendedBolus().delete(extendedBolus); @@ -1326,20 +1339,20 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { /* -{ - "_id": "5924898d577eb0880e355337", - "eventType": "Combo Bolus", - "duration": 120, - "splitNow": 0, - "splitExt": 100, - "enteredinsulin": 1, - "relative": 1, - "created_at": "2017-05-23T19:12:14Z", - "enteredBy": "AndroidAPS", - "NSCLIENT_ID": 1495566734628, - "mills": 1495566734000, - "mgdl": 106 -} + * { + * "_id": "5924898d577eb0880e355337", + * "eventType": "Combo Bolus", + * "duration": 120, + * "splitNow": 0, + * "splitExt": 100, + * "enteredinsulin": 1, + * "relative": 1, + * "created_at": "2017-05-23T19:12:14Z", + * "enteredBy": "AndroidAPS", + * "NSCLIENT_ID": 1495566734628, + * "mills": 1495566734000, + * "mgdl": 106 + * } */ public void createExtendedBolusFromJsonIfNotExists(JSONObject json) { @@ -1450,7 +1463,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { OverlappingIntervals offlineEvents = new OverlappingIntervals(); for (int i = 0; i < list.size(); i++) { CareportalEvent event = list.get(i); - if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) continue; + if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) + continue; offlineEvents.add(event); } @@ -1618,11 +1632,13 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (old != null) { if (!old.isEqual(profileSwitch)) { profileSwitch.source = old.source; - profileSwitch.profileName = old.profileName; // preserver profileName to prevent multiple CPP extension + profileSwitch.profileName = old.profileName; // preserver profileName to prevent multiple CPP + // extension getDaoProfileSwitch().delete(old); // need to delete/create because date may change too getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) - log.debug("PROFILESWITCH: Updating record by date from: " + Source.getString(profileSwitch.source) + " " + old.toString()); + log.debug("PROFILESWITCH: Updating record by date from: " + + Source.getString(profileSwitch.source) + " " + old.toString()); scheduleProfileSwitchChange(); return true; } @@ -1642,7 +1658,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(profileSwitch); getDaoProfileSwitch().create(old); if (L.isEnabled(L.DATABASE)) - log.debug("PROFILESWITCH: Updating record by _id from: " + Source.getString(profileSwitch.source) + " " + old.toString()); + log.debug("PROFILESWITCH: Updating record by _id from: " + + Source.getString(profileSwitch.source) + " " + old.toString()); scheduleProfileSwitchChange(); return true; } @@ -1652,14 +1669,16 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { profileSwitch.profileName = PercentageSplitter.pureName(profileSwitch.profileName); getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) - log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); + log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + + profileSwitch.toString()); scheduleProfileSwitchChange(); return true; } if (profileSwitch.source == Source.USER) { getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) - log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); + log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + + profileSwitch.toString()); scheduleProfileSwitchChange(); return true; } @@ -1701,17 +1720,18 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } - /* -{ - "_id":"592fa43ed97496a80da913d2", - "created_at":"2017-06-01T05:20:06Z", - "eventType":"Profile Switch", - "profile":"2016 +30%", - "units":"mmol", - "enteredBy":"sony", - "NSCLIENT_ID":1496294454309, -} - */ + + /* + * { + * "_id":"592fa43ed97496a80da913d2", + * "created_at":"2017-06-01T05:20:06Z", + * "eventType":"Profile Switch", + * "profile":"2016 +30%", + * "units":"mmol", + * "enteredBy":"sony", + * "NSCLIENT_ID":1496294454309, + * } + */ public void createProfileSwitchFromJsonIfNotExists(JSONObject trJson) { try { @@ -1796,6 +1816,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return null; } + // ---------------- Insight history handling --------------- public void createOrUpdate(InsightHistoryOffset offset) { @@ -1806,6 +1827,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } + public InsightHistoryOffset getInsightHistoryOffset(String pumpSerial) { try { return getDaoInsightHistoryOffset().queryForId(pumpSerial); @@ -1815,6 +1837,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return null; } + public void createOrUpdate(InsightBolusID bolusID) { try { getDaoInsightBolusID().createOrUpdate(bolusID); @@ -1823,19 +1846,19 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } + public InsightBolusID getInsightBolusID(String pumpSerial, int bolusID, long timestamp) { try { - return getDaoInsightBolusID().queryBuilder() - .where().eq("pumpSerial", pumpSerial) - .and().eq("bolusID", bolusID) - .and().between("timestamp", timestamp - 259200000, timestamp + 259200000) - .queryForFirst(); + return getDaoInsightBolusID().queryBuilder().where().eq("pumpSerial", pumpSerial).and() + .eq("bolusID", bolusID).and().between("timestamp", timestamp - 259200000, timestamp + 259200000) + .queryForFirst(); } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } + public void createOrUpdate(InsightPumpID pumpID) { try { getDaoInsightPumpID().createOrUpdate(pumpID); @@ -1844,14 +1867,12 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } + public InsightPumpID getPumpStoppedEvent(String pumpSerial, long before) { try { - return getDaoInsightPumpID().queryBuilder() - .orderBy("timestamp", false) - .where().eq("pumpSerial", pumpSerial) - .and().in("eventType", "PumpStopped", "PumpPaused") - .and().lt("timestamp", before) - .queryForFirst(); + return getDaoInsightPumpID().queryBuilder().orderBy("timestamp", false).where() + .eq("pumpSerial", pumpSerial).and().in("eventType", "PumpStopped", "PumpPaused").and() + .lt("timestamp", before).queryForFirst(); } catch (SQLException e) { log.error("Unhandled exception", e); } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 0ef71dac2a..0e8b26f781 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -3,16 +3,16 @@ package info.nightscout.androidaps.interfaces; import java.util.List; import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.data.Intervals; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.data.ProfileIntervals; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.plugins.treatments.Treatment; -import info.nightscout.androidaps.data.Intervals; -import info.nightscout.androidaps.data.ProfileIntervals; /** * Created by mike on 14.06.2016. @@ -20,47 +20,94 @@ import info.nightscout.androidaps.data.ProfileIntervals; public interface TreatmentsInterface { void updateTotalIOBTreatments(); + + void updateTotalIOBTempBasals(); + IobTotal getLastCalculationTreatments(); + + IobTotal getCalculationToTimeTreatments(long time); + + IobTotal getLastCalculationTempBasals(); + + IobTotal getCalculationToTimeTempBasals(long time, Profile profile); + MealData getMealData(); + List getTreatmentsFromHistory(); + + List getTreatments5MinBackFromHistory(long time); + + long getLastBolusTime(); + // real basals (not faked by extended bolus) boolean isInHistoryRealTempBasalInProgress(); + + TemporaryBasal getRealTempBasalFromHistory(long time); + boolean addToHistoryTempBasal(TemporaryBasal tempBasal); + // basal that can be faked by extended boluses boolean isTempBasalInProgress(); + + TemporaryBasal getTempBasalFromHistory(long time); + + Intervals getTemporaryBasalsFromHistory(); + boolean isInHistoryExtendedBoluslInProgress(); + + ExtendedBolus getExtendedBolusFromHistory(long time); + + Intervals getExtendedBolusesFromHistory(); + boolean addToHistoryExtendedBolus(ExtendedBolus extendedBolus); + boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate); + TempTarget getTempTargetFromHistory(); + + TempTarget getTempTargetFromHistory(long time); + + Intervals getTempTargetsFromHistory(); + + void addToHistoryTempTarget(TempTarget tempTarget); + ProfileSwitch getProfileSwitchFromHistory(long time); + + ProfileIntervals getProfileSwitchesFromHistory(); + + void addToHistoryProfileSwitch(ProfileSwitch profileSwitch); + long oldestDataAvailable(); + + List getTreatmentsFromHistoryXMinutesAgo(int minutesAgo); + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index 28f83dfb33..266debc4c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -1712,12 +1712,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, public int getBackgroundColor(OverviewColorScheme scheme) { - return MainApp.gc(scheme.getBackground(isNewColor)); + return MainApp.gc(scheme.getBackground(useNewRibbonColors)); } public int getTextColor(OverviewColorScheme scheme) { - return MainApp.gc(scheme.getTextColor(isNewColor)); + return MainApp.gc(scheme.getTextColor(useNewRibbonColors)); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java index ca24c4ed9e..a401edbfa3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.general.wear; import android.content.Context; import android.content.Intent; +import android.util.Log; import com.squareup.otto.Subscribe; @@ -35,11 +36,14 @@ public class WearPlugin extends PluginBase { private final Context ctx; private static WearPlugin wearPlugin; + private static String TAG = "WearPlugin"; + public static WearPlugin getPlugin() { return wearPlugin; } + public static WearPlugin initPlugin(Context ctx) { if (wearPlugin == null) { @@ -49,18 +53,15 @@ public class WearPlugin extends PluginBase { return wearPlugin; } + WearPlugin(Context ctx) { - super(new PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(WearFragment.class.getName()) - .pluginName(R.string.wear) - .shortName(R.string.wear_shortname) - .preferencesId(R.xml.pref_wear) - .description(R.string.description_wear) - ); + super(new PluginDescription().mainType(PluginType.GENERAL).fragmentClass(WearFragment.class.getName()) + .pluginName(R.string.wear).shortName(R.string.wear_shortname).preferencesId(R.xml.pref_wear) + .description(R.string.description_wear)); this.ctx = ctx; } + @Override protected void onStart() { MainApp.bus().register(this); @@ -70,38 +71,53 @@ public class WearPlugin extends PluginBase { super.onStart(); } + @Override protected void onStop() { MainApp.bus().unregister(this); } + private void sendDataToWatch(boolean status, boolean basals, boolean bgValue) { - if (isEnabled(getType())) { //only start service when this plugin is enabled + + //Log.d(TAG, "WR: WearPlugin:sendDataToWatch (status=" + status + ",basals=" + basals + ",bgValue=" + bgValue + ")"); + + if (isEnabled(getType())) { // only start service when this plugin is enabled if (bgValue) { ctx.startService(new Intent(ctx, WatchUpdaterService.class)); } if (basals) { - ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BASALS)); + ctx.startService(new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_BASALS)); } if (status) { - ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_STATUS)); + ctx.startService(new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_STATUS)); } } } + void resendDataToWatch() { + //Log.d(TAG, "WR: WearPlugin:resendDataToWatch"); ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_RESEND)); } + void openSettings() { + //Log.d(TAG, "WR: WearPlugin:openSettings"); ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)); } + void requestNotificationCancel(String actionstring) { - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION); + //Log.d(TAG, "WR: WearPlugin:requestNotificationCancel"); + + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION); intent.putExtra("actionstring", actionstring); ctx.startService(intent); } @@ -115,36 +131,43 @@ public class WearPlugin extends PluginBase { sendDataToWatch(true, false, false); } + @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { sendDataToWatch(true, true, false); } + @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { sendDataToWatch(true, true, false); } + @Subscribe public void onStatusEvent(final EventOpenAPSUpdateGui ev) { sendDataToWatch(true, true, false); } + @Subscribe public void onStatusEvent(final EventExtendedBolusChange ev) { sendDataToWatch(true, true, false); } + @Subscribe public void onStatusEvent(final EventNewBG ev) { sendDataToWatch(true, true, true); } + @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { sendDataToWatch(false, true, false); } + @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { if (WatchUpdaterService.shouldReportLoopStatus(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) { @@ -156,26 +179,31 @@ public class WearPlugin extends PluginBase { @Subscribe public void onStatusEvent(final EventOverviewBolusProgress ev) { if (!ev.isSMB() || SP.getBoolean("wear_notifySMB", true)) { - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); intent.putExtra("progresspercent", ev.percent); intent.putExtra("progressstatus", ev.status); ctx.startService(intent); } } + @Subscribe public void onStatusEvent(final EventBolusRequested ev) { String status = String.format(MainApp.gs(R.string.bolusrequested), ev.getAmount()); - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); intent.putExtra("progresspercent", 0); intent.putExtra("progressstatus", status); ctx.startService(intent); } + @Subscribe public void onStatusEvent(final EventDismissBolusprogressIfRunning ev) { - if (ev.result == null) return; + if (ev.result == null) + return; String status; if (ev.result.success) { @@ -183,34 +211,41 @@ public class WearPlugin extends PluginBase { } else { status = MainApp.gs(R.string.nosuccess); } - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS); intent.putExtra("progresspercent", 100); intent.putExtra("progressstatus", status); ctx.startService(intent); } + public void requestActionConfirmation(String title, String message, String actionstring) { - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST); + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST); intent.putExtra("title", title); intent.putExtra("message", message); intent.putExtra("actionstring", actionstring); ctx.startService(intent); } + public void requestChangeConfirmation(String title, String message, String actionstring) { - Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST); + Intent intent = new Intent(ctx, WatchUpdaterService.class) + .setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST); intent.putExtra("title", title); intent.putExtra("message", message); intent.putExtra("actionstring", actionstring); ctx.startService(intent); } + public static void registerWatchUpdaterService(WatchUpdaterService wus) { watchUS = wus; } + public static void unRegisterWatchUpdaterService() { watchUS = null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java index d935bafec6..e94aa286e5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + import android.os.AsyncTask; import android.util.Log; @@ -12,41 +15,126 @@ import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; -import java.util.concurrent.TimeUnit; - /** * Created by emmablack on 12/26/14. */ -class SendToDataLayerThread extends AsyncTask { +class SendToDataLayerThread extends AsyncTask { + private GoogleApiClient googleApiClient; - private static final String TAG = "SendDataThread"; - String path; + private static final String TAG = "SendToDataLayerThread"; + private String path; + private String logPrefix = ""; // "WR: "; + private static int concurrency = 0; + private static int state = 0; + private static final ReentrantLock lock = new ReentrantLock(); + private static long lastlock = 0; + private static final boolean testlockup = false; // always false in production + SendToDataLayerThread(String path, GoogleApiClient pGoogleApiClient) { + // Log.d(TAG, logPrefix + "SendToDataLayerThread: " + path); this.path = path; googleApiClient = pGoogleApiClient; } + + @Override + protected void onPreExecute() { + concurrency++; + if ((concurrency > 12) || ((concurrency > 3 && (lastlock != 0) && (tsl() - lastlock) > 300000))) { + // error if 9 concurrent threads or lock held for >5 minutes with concurrency of 4 + final String err = "Wear Integration deadlock detected!! " + ((lastlock != 0) ? "locked" : "") + " state:" + + state + " @" + hourMinuteString(tsl()); + // Home.toaststaticnext(err); + Log.e(TAG, logPrefix + err); + } + if (concurrency < 0) + Log.d(TAG, logPrefix + "Wear Integration impossible concurrency!!"); + + Log.d(TAG, logPrefix + "SendDataToLayerThread pre-execute concurrency: " + concurrency); + } + + @Override protected Void doInBackground(DataMap... params) { - try { - final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(15, TimeUnit.SECONDS); - for (Node node : nodes.getNodes()) { - for (DataMap dataMap : params) { - PutDataMapRequest putDMR = PutDataMapRequest.create(path); - putDMR.getDataMap().putAll(dataMap); - PutDataRequest request = putDMR.asPutDataRequest(); - DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleApiClient, request).await(15, TimeUnit.SECONDS); - if (result.getStatus().isSuccess()) { - Log.d(TAG, "DataMap: " + dataMap + " sent to: " + node.getDisplayName()); - } else { - Log.d(TAG, "ERROR: failed to send DataMap"); - } - } + if (testlockup) { + try { + Log.e(TAG, logPrefix + "WARNING RUNNING TEST LOCK UP CODE - NEVER FOR PRODUCTION"); + Thread.sleep(1000000); // DEEEBBUUGGGG + } catch (Exception e) { } - } catch (Exception e) { - Log.e(TAG, "Got exception sending data to wear: " + e.toString()); } + sendToWear(params); + concurrency--; + Log.d(TAG, logPrefix + "SendDataToLayerThread post-execute concurrency: " + concurrency); return null; } + + + // Debug function to expose where it might be locking up + private synchronized void sendToWear(final DataMap... params) { + if (!lock.tryLock()) { + Log.d(TAG, logPrefix + "Concurrent access - waiting for thread unlock"); + lock.lock(); // enforce single threading + Log.d(TAG, logPrefix + "Thread unlocked - proceeding"); + } + lastlock = tsl(); + try { + if (state != 0) { + Log.e(TAG, logPrefix + "WEAR STATE ERROR: state=" + state); + } + state = 1; + final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(15, + TimeUnit.SECONDS); + + Log.d(TAG, logPrefix + "Nodes: " + nodes); + + state = 2; + for (Node node : nodes.getNodes()) { + state = 3; + for (DataMap dataMap : params) { + state = 4; + PutDataMapRequest putDMR = PutDataMapRequest.create(path); + state = 5; + putDMR.getDataMap().putAll(dataMap); + putDMR.setUrgent(); + state = 6; + PutDataRequest request = putDMR.asPutDataRequest(); + state = 7; + DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleApiClient, request).await(15, + TimeUnit.SECONDS); + state = 8; + if (result.getStatus().isSuccess()) { + Log.d(TAG, logPrefix + "DataMap: " + dataMap + " sent to: " + node.getDisplayName()); + } else { + Log.e(TAG, logPrefix + "ERROR: failed to send DataMap"); + result = Wearable.DataApi.putDataItem(googleApiClient, request).await(30, TimeUnit.SECONDS); + if (result.getStatus().isSuccess()) { + Log.d(TAG, logPrefix + "DataMap retry: " + dataMap + " sent to: " + node.getDisplayName()); + } else { + Log.e(TAG, logPrefix + "ERROR on retry: failed to send DataMap: " + + result.getStatus().toString()); + } + } + state = 9; + } + } + state = 0; + } catch (Exception e) { + Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e.toString()); + } finally { + lastlock = 0; + lock.unlock(); + } + } + + + private static long tsl() { + return System.currentTimeMillis(); + } + + + private static String hourMinuteString(long timestamp) { + return android.text.format.DateFormat.format("kk:mm", timestamp).toString(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java index 12dc116895..42ab57eedd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java @@ -1,9 +1,17 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.os.AsyncTask; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; @@ -14,19 +22,16 @@ import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.wearable.CapabilityApi; +import com.google.android.gms.wearable.CapabilityInfo; import com.google.android.gms.wearable.DataMap; import com.google.android.gms.wearable.MessageEvent; +import com.google.android.gms.wearable.Node; import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -37,36 +42,42 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; import info.nightscout.androidaps.plugins.general.wear.WearPlugin; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.treatments.Treatment; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SafeParse; import info.nightscout.androidaps.utils.ToastUtils; -public class WatchUpdaterService extends WearableListenerService implements - GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener { +public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { + + private static final Logger log = LoggerFactory.getLogger(WatchUpdaterService.class); + private static final String TAG = "WatchUpdaterService"; + + // ACTIONS public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend"); public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings"); public static final String ACTION_SEND_STATUS = WatchUpdaterService.class.getName().concat(".SendStatus"); public static final String ACTION_SEND_BASALS = WatchUpdaterService.class.getName().concat(".SendBasals"); public static final String ACTION_SEND_BOLUSPROGRESS = WatchUpdaterService.class.getName().concat(".BolusProgress"); - public static final String ACTION_SEND_ACTIONCONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ActionConfirmationRequest"); - public static final String ACTION_SEND_CHANGECONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ChangeConfirmationRequest"); - public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification"); + public static final String ACTION_SEND_ACTIONCONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat( + ".ActionConfirmationRequest"); + public static final String ACTION_SEND_CHANGECONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat( + ".ChangeConfirmationRequest"); + public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat( + ".CancelNotification"); - private GoogleApiClient googleApiClient; + // PATHS public static final String WEARABLE_DATA_PATH = "/nightscout_watch_data"; public static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend"; private static final String WEARABLE_CANCELBOLUS_PATH = "/nightscout_watch_cancel_bolus"; @@ -81,16 +92,25 @@ public class WatchUpdaterService extends WearableListenerService implements public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest"; public static final String ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest"; public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; + // Phone - Capabilites + private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs"; + private static final String MESSAGE_PATH_PHONE = "/phone_message_path"; + // Wear - Capabilites + private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs"; + private static final String MESSAGE_PATH_WEAR = "/wear_message_path"; - + private GoogleApiClient googleApiClient; boolean wear_integration = false; SharedPreferences mPrefs; private static boolean lastLoopStatus; - private static Logger log = LoggerFactory.getLogger(WatchUpdaterService.class); - private Handler handler; + private String mWearNodeId = null; + private String localnode = null; + private String logPrefix = ""; // "WR: " + + @Override public void onCreate() { mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); @@ -106,38 +126,43 @@ public class WatchUpdaterService extends WearableListenerService implements } } + public void listenForChangeInSettings() { WearPlugin.registerWatchUpdaterService(this); } + public void setSettings() { wear_integration = WearPlugin.getPlugin().isEnabled(PluginType.GENERAL); + // Log.d(TAG, "WR: wear_integration=" + wear_integration); if (wear_integration) { googleApiConnect(); } } - public void googleApiConnect() { + + private void googleApiConnect() { if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) { googleApiClient.disconnect(); } - googleApiClient = new GoogleApiClient.Builder(this) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .addApi(Wearable.API) - .build(); + googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this) + .addOnConnectionFailedListener(this).addApi(Wearable.API).build(); Wearable.MessageApi.addListener(googleApiClient, this); if (googleApiClient.isConnected()) { - Log.d("WatchUpdater", "API client is connected"); + Log.d(TAG, logPrefix + "API client is connected"); } else { + // Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect"); googleApiClient.connect(); } } + @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent != null ? intent.getAction() : null; + // Log.d(TAG, logPrefix + "onStartCommand: " + action); + if (wear_integration) { handler.post(() -> { if (googleApiClient.isConnected()) { @@ -150,7 +175,8 @@ public class WatchUpdaterService extends WearableListenerService implements } else if (ACTION_SEND_BASALS.equals(action)) { sendBasals(); } else if (ACTION_SEND_BOLUSPROGRESS.equals(action)) { - sendBolusProgress(intent.getIntExtra("progresspercent", 0), intent.hasExtra("progressstatus") ? intent.getStringExtra("progressstatus") : ""); + sendBolusProgress(intent.getIntExtra("progresspercent", 0), + intent.hasExtra("progressstatus") ? intent.getStringExtra("progressstatus") : ""); } else if (ACTION_SEND_ACTIONCONFIRMATIONREQUEST.equals(action)) { String title = intent.getStringExtra("title"); String message = intent.getStringExtra("message"); @@ -177,13 +203,63 @@ public class WatchUpdaterService extends WearableListenerService implements } + private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) { + Log.d("WatchUpdaterService", logPrefix + "CabilityInfo: " + capabilityInfo); + Set connectedNodes = capabilityInfo.getNodes(); + mWearNodeId = pickBestNodeId(connectedNodes); + } + + + private String pickBestNodeId(Set nodes) { + String bestNodeId = null; + // Find a nearby node or pick one arbitrarily + for (Node node : nodes) { + if (node.isNearby()) { + return node.getId(); + } + bestNodeId = node.getId(); + } + return bestNodeId; + } + + @Override public void onConnected(Bundle connectionHint) { + CapabilityApi.CapabilityListener capabilityListener = capabilityInfo -> { + updateWearSyncBgsCapability(capabilityInfo); + // Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mWearNodeID:" + mWearNodeId); + // new CheckWearableConnected().execute(); + }; + + Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_WEAR_APP); sendData(); } + + @Override + public void onPeerConnected(com.google.android.gms.wearable.Node peer) {// KS + super.onPeerConnected(peer); + String id = peer.getId(); + String name = peer.getDisplayName(); + // Log.d(TAG, logPrefix + "onPeerConnected peer name & ID: " + name + "|" + id); + } + + + @Override + public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {// KS + super.onPeerDisconnected(peer); + String id = peer.getId(); + String name = peer.getDisplayName(); + // Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + } + + @Override public void onMessageReceived(MessageEvent event) { + + // Log.d(TAG, logPrefix + "onMessageRecieved: " + event); + if (wear_integration) { if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) { resendData(); @@ -207,13 +283,16 @@ public class WatchUpdaterService extends WearableListenerService implements } } + private void cancelBolus() { ConfigBuilderPlugin.getPlugin().getActivePump().stopBolusDelivering(); } + private void sendData() { BgReading lastBG = DatabaseHelper.lastBg(); + // Log.d(TAG, logPrefix + "LastBg=" + lastBG); if (lastBG != null) { GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); @@ -228,18 +307,19 @@ public class WatchUpdaterService extends WearableListenerService implements return; } - new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(dataMap); + executeTask(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient), dataMap); } } } + private DataMap dataMapSingleBG(BgReading lastBG, GlucoseStatus glucoseStatus) { String units = ProfileFunctions.getInstance().getProfileUnits(); Double lowLine = SafeParse.stringToDouble(mPrefs.getString("low_mark", "0")); Double highLine = SafeParse.stringToDouble(mPrefs.getString("high_mark", "0")); - //convert to mg/dl + // convert to mg/dl if (!units.equals(Constants.MGDL)) { lowLine *= Constants.MMOLL_TO_MGDL; highLine *= Constants.MMOLL_TO_MGDL; @@ -271,8 +351,10 @@ public class WatchUpdaterService extends WearableListenerService implements dataMap.putString("avgDelta", "--"); } else { dataMap.putString("slopeArrow", slopeArrow(glucoseStatus.delta)); - dataMap.putString("delta", deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); - dataMap.putString("avgDelta", deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units)); + dataMap.putString("delta", + deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); + dataMap.putString("avgDelta", + deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units)); } dataMap.putLong("sgvLevel", sgvLevel); dataMap.putDouble("sgvDouble", lastBG.value); @@ -281,13 +363,13 @@ public class WatchUpdaterService extends WearableListenerService implements return dataMap; } + private String deltastring(double deltaMGDL, double deltaMMOL, String units) { String deltastring = ""; if (deltaMGDL >= 0) { deltastring += "+"; } else { deltastring += "-"; - } boolean detailed = SP.getBoolean("wear_detailed_delta", false); @@ -307,6 +389,7 @@ public class WatchUpdaterService extends WearableListenerService implements return deltastring; } + private String slopeArrow(double delta) { if (delta <= (-3.5 * 5)) { return "\u21ca"; @@ -330,10 +413,11 @@ public class WatchUpdaterService extends WearableListenerService implements if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); } - long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5); + long startTime = System.currentTimeMillis() - (long)(60000 * 60 * 5.5); BgReading last_bg = DatabaseHelper.lastBg(); - if (last_bg == null) return; + if (last_bg == null) + return; List graph_bgs = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, true); GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(true); @@ -352,28 +436,29 @@ public class WatchUpdaterService extends WearableListenerService implements } } entries.putDataMapArrayList("entries", dataMaps); - new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(entries); + executeTask(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient), entries); + + // Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } - sendPreferences(); + sendPreferences(); // DMR sendBasals(); - sendStatus(); + sendStatus(); // DMR } + private void sendBasals() { if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); } long now = System.currentTimeMillis(); - final long startTimeWindow = now - (long) (60000 * 60 * 5.5); - + final long startTimeWindow = now - (long)(60000 * 60 * 5.5); ArrayList basals = new ArrayList<>(); ArrayList temps = new ArrayList<>(); ArrayList boluses = new ArrayList<>(); ArrayList predictions = new ArrayList<>(); - Profile profile = ProfileFunctions.getInstance().getProfile(); if (profile == null) { @@ -401,33 +486,32 @@ public class WatchUpdaterService extends WearableListenerService implements } } - for (; runningTime < now; runningTime += 5 * 60 * 1000) { Profile profileTB = ProfileFunctions.getInstance().getProfile(runningTime); - //basal rate + // basal rate endBasalValue = profile.getBasal(runningTime); if (endBasalValue != beginBasalValue) { - //push the segment we recently left + // push the segment we recently left basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); - //begin new Basal segment + // begin new Basal segment beginBasalSegmentTime = runningTime; beginBasalValue = endBasalValue; } - //temps + // temps tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(runningTime); if (tb1 == null && tb2 == null) { - //no temp stays no temp + // no temp stays no temp } else if (tb1 != null && tb2 == null) { - //temp is over -> push it + // temp is over -> push it temps.add(tempDatamap(tb_start, tb_before, runningTime, endBasalValue, tb_amount)); tb1 = null; } else if (tb1 == null && tb2 != null) { - //temp begins + // temp begins tb1 = tb2; tb_start = runningTime; tb_before = endBasalValue; @@ -445,16 +529,16 @@ public class WatchUpdaterService extends WearableListenerService implements } } if (beginBasalSegmentTime != runningTime) { - //push the remaining segment + // push the remaining segment basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); } if (tb1 != null) { - tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); //use "now" to express current situation + tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); // use "now" to express current situation if (tb2 == null) { - //express the cancelled temp by painting it down one minute early + // express the cancelled temp by painting it down one minute early temps.add(tempDatamap(tb_start, tb_before, now - 1 * 60 * 1000, endBasalValue, tb_amount)); } else { - //express currently running temp by painting it a bit into the future + // express currently running temp by painting it a bit into the future Profile profileNow = ProfileFunctions.getInstance().getProfile(now); double currentAmount = tb2.tempBasalConvertedToAbsolute(now, profileNow); if (currentAmount != tb_amount) { @@ -465,45 +549,49 @@ public class WatchUpdaterService extends WearableListenerService implements } } } else { - tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); //use "now" to express current situation + tb2 = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); // use "now" to express current situation if (tb2 != null) { - //onset at the end + // onset at the end Profile profileTB = ProfileFunctions.getInstance().getProfile(runningTime); double currentAmount = tb2.tempBasalConvertedToAbsolute(runningTime, profileTB); - temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); + temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, + currentAmount)); } } List treatments = TreatmentsPlugin.getPlugin().getTreatmentsFromHistory(); for (Treatment treatment : treatments) { if (treatment.date > startTimeWindow) { - boluses.add(treatmentMap(treatment.date, treatment.insulin, treatment.carbs, treatment.isSMB, treatment.isValid)); + boluses.add(treatmentMap(treatment.date, treatment.insulin, treatment.carbs, treatment.isSMB, + treatment.isValid)); } } final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; - if (SP.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.request.hasPredictions && finalLastRun.constraintsProcessed != null) { + if (SP.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.request.hasPredictions + && finalLastRun.constraintsProcessed != null) { List predArray = finalLastRun.constraintsProcessed.getPredictions(); if (!predArray.isEmpty()) { for (BgReading bg : predArray) { - if (bg.value < 40) continue; + if (bg.value < 40) + continue; predictions.add(predictionMap(bg.date, bg.value, bg.getPredectionColor())); } } } - DataMap dm = new DataMap(); dm.putDataMapArrayList("basals", basals); dm.putDataMapArrayList("temps", temps); dm.putDataMapArrayList("boluses", boluses); dm.putDataMapArrayList("predictions", predictions); - new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient).execute(dm); + executeTask(new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient), dm); } + private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) { DataMap dm = new DataMap(); dm.putLong("starttime", startTime); @@ -514,6 +602,7 @@ public class WatchUpdaterService extends WearableListenerService implements return dm; } + private DataMap basalMap(long startTime, long endTime, double amount) { DataMap dm = new DataMap(); dm.putLong("starttime", startTime); @@ -522,6 +611,7 @@ public class WatchUpdaterService extends WearableListenerService implements return dm; } + private DataMap treatmentMap(long date, double bolus, double carbs, boolean isSMB, boolean isValid) { DataMap dm = new DataMap(); dm.putLong("date", date); @@ -532,6 +622,7 @@ public class WatchUpdaterService extends WearableListenerService implements return dm; } + private DataMap predictionMap(long timestamp, double sgv, int color) { DataMap dm = new DataMap(); dm.putLong("timestamp", timestamp); @@ -544,35 +635,39 @@ public class WatchUpdaterService extends WearableListenerService implements private void sendNotification() { if (googleApiClient.isConnected()) { PutDataMapRequest dataMapRequest = PutDataMapRequest.create(OPEN_SETTINGS_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putString("openSettings", "openSettings"); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendNotification", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("OpenSettings", "No connection to wearable available!"); } } + private void sendBolusProgress(int progresspercent, String status) { if (googleApiClient.isConnected()) { PutDataMapRequest dataMapRequest = PutDataMapRequest.create(BOLUS_PROGRESS_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putString("bolusProgress", "bolusProgress"); dataMapRequest.getDataMap().putString("progressstatus", status); dataMapRequest.getDataMap().putInt("progresspercent", progresspercent); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendBolusProgress", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("BolusProgress", "No connection to wearable available!"); } } + private void sendActionConfirmationRequest(String title, String message, String actionstring) { if (googleApiClient.isConnected()) { PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CONFIRMATION_REQUEST_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putString("actionConfirmationRequest", "actionConfirmationRequest"); dataMapRequest.getDataMap().putString("title", title); @@ -582,16 +677,18 @@ public class WatchUpdaterService extends WearableListenerService implements log.debug("Requesting confirmation from wear: " + actionstring); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendActionConfirmationRequest", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("confirmationRequest", "No connection to wearable available!"); } } + private void sendChangeConfirmationRequest(String title, String message, String actionstring) { if (googleApiClient.isConnected()) { PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CHANGECONFIRMATION_REQUEST_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest"); dataMapRequest.getDataMap().putString("title", title); @@ -601,16 +698,18 @@ public class WatchUpdaterService extends WearableListenerService implements log.debug("Requesting confirmation from wear: " + actionstring); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendChangeConfirmationRequest", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("changeConfirmRequest", "No connection to wearable available!"); } } + private void sendCancelNotificationRequest(String actionstring) { if (googleApiClient.isConnected()) { PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CANCELNOTIFICATION_REQUEST_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest"); dataMapRequest.getDataMap().putString("actionstring", actionstring); @@ -618,12 +717,14 @@ public class WatchUpdaterService extends WearableListenerService implements log.debug("Canceling notification on wear: " + actionstring); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendCancelNotificationRequest", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { - Log.e("cancelNotificationRequest", "No connection to wearable available!"); + Log.e("cancelNotificationReq", "No connection to wearable available!"); } } + private void sendStatus() { if (googleApiClient.isConnected()) { @@ -639,12 +740,13 @@ public class WatchUpdaterService extends WearableListenerService implements IobTotal basalIob = treatmentsInterface.getLastCalculationTempBasals().round(); iobSum = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob); - iobDetail = "(" + DecimalFormatter.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")"; - cobString = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "WatcherUpdaterService").generateCOBString(); + iobDetail = "(" + DecimalFormatter.to2Decimal(bolusIob.iob) + "|" + + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")"; + cobString = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "WatcherUpdaterService") + .generateCOBString(); currentBasal = generateBasalString(treatmentsInterface); - //bgi - + // bgi double bgi = -(bolusIob.activity + basalIob.activity) * 5 * profile.getIsf(); bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.to1Decimal(bgi); @@ -652,24 +754,23 @@ public class WatchUpdaterService extends WearableListenerService implements status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); } - - //batteries + // batteries int phoneBattery = getBatteryLevel(getApplicationContext()); String rigBattery = NSDeviceStatus.getInstance().getUploaderStatus().trim(); - long openApsStatus = -1; - //OpenAPS status + // OpenAPS status if (Config.APS) { - //we are AndroidAPS - openApsStatus = LoopPlugin.lastRun != null && LoopPlugin.lastRun.lastEnact != null && LoopPlugin.lastRun.lastEnact.getTime() != 0 ? LoopPlugin.lastRun.lastEnact.getTime() : -1; + // we are AndroidAPS + openApsStatus = LoopPlugin.lastRun != null && LoopPlugin.lastRun.lastEnact != null + && LoopPlugin.lastRun.lastEnact.getTime() != 0 ? LoopPlugin.lastRun.lastEnact.getTime() : -1; } else { - //NSClient or remote + // NSClient or remote openApsStatus = NSDeviceStatus.getOpenApsTimestamp(); } PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putString("externalStatusString", status); dataMapRequest.getDataMap().putString("iobSum", iobSum); dataMapRequest.getDataMap().putString("iobDetail", iobDetail); @@ -683,30 +784,50 @@ public class WatchUpdaterService extends WearableListenerService implements dataMapRequest.getDataMap().putBoolean("showBgi", mPrefs.getBoolean("wear_showbgi", false)); dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendStatus", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("SendStatus", "No connection to wearable available!"); } } + private void sendPreferences() { if (googleApiClient.isConnected()) { boolean wearcontrol = SP.getBoolean("wearcontrol", false); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH); - //unique content + // unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putBoolean("wearcontrol", wearcontrol); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + debugData("sendPreferences", putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { Log.e("SendStatus", "No connection to wearable available!"); } } + + private void debugData(String source, Object data) { + // Log.d(TAG, "WR: " + source + " " + data); + } + + + private void executeTask(AsyncTask task, DataMap... parameters) { + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])parameters); + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + // } else { + // task.execute(); + // } + } + + @NonNull - private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, String bgiString) { + private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, + String bgiString) { String status = ""; @@ -733,7 +854,7 @@ public class WatchUpdaterService extends WearableListenerService implements status += currentBasal + " " + iobString; - //add BGI if shown, otherwise return + // add BGI if shown, otherwise return if (mPrefs.getBoolean("wear_showbgi", false)) { status += " " + bgiString; } @@ -741,6 +862,7 @@ public class WatchUpdaterService extends WearableListenerService implements return status; } + @NonNull private String generateBasalString(TreatmentsInterface treatmentsInterface) { @@ -763,6 +885,7 @@ public class WatchUpdaterService extends WearableListenerService implements return basalStringResult; } + @Override public void onDestroy() { if (googleApiClient != null && googleApiClient.isConnected()) { @@ -771,18 +894,22 @@ public class WatchUpdaterService extends WearableListenerService implements WearPlugin.unRegisterWatchUpdaterService(); } + @Override public void onConnectionSuspended(int cause) { } + @Override public void onConnectionFailed(ConnectionResult connectionResult) { } + public static boolean shouldReportLoopStatus(boolean enabled) { return (lastLoopStatus != enabled); } + public static int getBatteryLevel(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); if (batteryIntent != null) { @@ -791,7 +918,7 @@ public class WatchUpdaterService extends WearableListenerService implements if (level == -1 || scale == -1) { return 50; } - return (int) (((float) level / (float) scale) * 100.0f); + return (int)(((float)level / (float)scale) * 100.0f); } return 50; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index 224444ff29..ca55524b5f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -198,6 +198,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter } + // TODO remove private void migrateSettings() { if ("US (916 MHz)".equals(SP.getString(MedtronicConst.Prefs.PumpFrequency, null))) { @@ -457,6 +458,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter this.pumpState = PumpDriverState.Connected; + // time (1h) + medtronicUIComm.executeCommand(MedtronicCommandType.RealTimeClock); + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 30); + readPumpHistory(); // TODO rewrite reading of data to be done in background or different thread perhaps ?? @@ -472,10 +477,6 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter // configuration (once and then if history shows config changes) medtronicUIComm.executeCommand(MedtronicCommandType.getSettings(MedtronicUtil.getMedtronicPumpModel())); - // time (1h) - medtronicUIComm.executeCommand(MedtronicCommandType.RealTimeClock); - scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 30); - // read profile (once, later its controlled by isThisProfileSet method) medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD); @@ -511,86 +512,12 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter @Override public boolean isThisProfileSet(Profile profile) { - return isThisProfileSet_New(profile); - } - - - @Deprecated - public boolean isThisProfileSet_Old(Profile profile) { - - if (!this.isInitialized) { - return true; - } - - // LOG.info("isThisProfileSet: check"); - - LOG.info("isThisProfileSet: check [basalProfileChanged={}, basalByHourSet={}, isBasalProfileInvalid={}", - basalProfileChanged, getMDTPumpStatus().basalsByHour != null, isBasalProfileInvalid); - - if (!basalProfileChanged && getMDTPumpStatus().basalsByHour != null && !isBasalProfileInvalid) { - if (isLoggingEnabled()) - LOG.debug("isThisProfileSet: profile has not changed and is not invalid."); - - return isProfileSame(profile); - } - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - MedtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable); - setRefreshButtonEnabled(true); - - return true; // we don't won't setting profile if pump unreachable - } - - MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); - - if (isLoggingEnabled()) - LOG.debug("isThisProfileSet: profile possible changed, reading from Pump."); - - MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD); - - boolean valid = false; - boolean noData = false; - - LOG.debug("isThisProfileSet: haveData={}", responseTask.hasData()); - - if (responseTask.hasData()) { - - valid = isProfileSame(profile); - - LOG.debug("isThisProfileSet: valid={}", valid); - - if (valid) { - basalProfileChanged = false; - } - - } else { - noData = true; - - if (isLoggingEnabled()) - LOG.debug("Basal profile NO DATA"); - } - - isBasalProfileInvalid = !valid; - - setRefreshButtonEnabled(true); - - // we don't want to force set profile if we couldn't read the profile (noData) - - return (noData || valid); - } - - - public boolean isThisProfileSet_New(Profile profile) { - LOG.debug("isThisProfileSet: basalInitalized={}", getMDTPumpStatus().basalProfileStatus); if (getMDTPumpStatus().basalProfileStatus != BasalProfileStatus.ProfileOK) return true; return isProfileSame(profile); - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java index 448a8b6210..3b9c58d17e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java @@ -560,8 +560,8 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager rawHistoryPage.dumpToDebug(); - List medtronicHistoryEntries = pumpHistoryDecoder.processPageAndCreateRecords( - rawHistoryPage, false, PumpHistoryEntry.class); + List medtronicHistoryEntries = pumpHistoryDecoder + .processPageAndCreateRecords(rawHistoryPage); LOG.debug("getPumpHistory: Found {} history entries.", medtronicHistoryEntries.size()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java index 30e737af88..1c6d2d82c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java @@ -34,7 +34,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; *

* Author: Andy {andy@atech-software.com} */ -public abstract class MedtronicHistoryDecoder { +public abstract class MedtronicHistoryDecoder implements MedtronicHistoryDecoderInterface { private static final Logger LOG = LoggerFactory.getLogger(MedtronicHistoryDecoder.class); @@ -51,8 +51,9 @@ public abstract class MedtronicHistoryDecoder { } - public abstract RecordDecodeStatus decodeRecord(MedtronicHistoryEntry record); + // public abstract Class getHistoryEntryClass(); + // public abstract RecordDecodeStatus decodeRecord(T record); public abstract void postProcess(); @@ -92,7 +93,7 @@ public abstract class MedtronicHistoryDecoder { // TODO_ extend this to also use bigger pages (for now we support only 1024 // pages) - public List checkPage(RawHistoryPage page, boolean partial) throws RuntimeException { + private List checkPage(RawHistoryPage page, boolean partial) throws RuntimeException { List byteList = new ArrayList(); // if (!partial && page.getData().length != 1024 /* page.commandType.getRecordLength() */) { @@ -120,9 +121,8 @@ public abstract class MedtronicHistoryDecoder { // public abstract List processPageAndCreateRecords(RawHistoryPage page, // boolean partial) throws Exception; - public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, - Class clazz) throws Exception { - return processPageAndCreateRecords(rawHistoryPage, false, clazz); + public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage) { + return processPageAndCreateRecords(rawHistoryPage, false); } @@ -213,12 +213,19 @@ public abstract class MedtronicHistoryDecoder { } - public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, - boolean partial, Class clazz) { - List dataClear = checkPage(rawHistoryPage, partial); - List records = createRecords(dataClear, clazz); + // public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage) { + // return processPageAndCreateRecords(rawHistoryPage, false, getHistoryEntryClass()); + // } - for (MedtronicHistoryEntry record : records) { + // public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, boolean partial) { + // return processPageAndCreateRecords(rawHistoryPage, partial, getHistoryEntryClass()); + // } + + private List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, boolean partial) { + List dataClear = checkPage(rawHistoryPage, partial); + List records = createRecords(dataClear); + + for (T record : records) { decodeRecord(record); } @@ -227,7 +234,6 @@ public abstract class MedtronicHistoryDecoder { return records; } - - protected abstract List createRecords(List dataClear, Class clazz); + // public abstract List createRecords(List dataClear); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java new file mode 100644 index 0000000000..a00602c027 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import java.util.List; + +/** + * Created by andy on 3/10/19. + */ + +public interface MedtronicHistoryDecoderInterface { + + RecordDecodeStatus decodeRecord(T record); + + + List createRecords(List dataClear); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java index 4ce8da9a1c..0cdf8da6b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java @@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus; /** @@ -35,7 +34,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDeco * Author: Andy {andy@atech-software.com} */ -public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { +public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { private static final Logger LOG = LoggerFactory.getLogger(MedtronicCGMSHistoryDecoder.class); @@ -46,7 +45,17 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { } - public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn) { + // @Override + // public Class getHistoryEntryClass() { + // return CGMSHistoryEntry.class; + // } + + // @Override + // public Class getHistoryEntryClass() { + // return CGMSHistoryEntry.class; + // } + + public RecordDecodeStatus decodeRecord(CGMSHistoryEntry entryIn) { CGMSHistoryEntry precord = (CGMSHistoryEntry)entryIn; try { return decodeRecord(precord, false); @@ -110,7 +119,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { } - protected List createRecords(List dataClearInput, Class clazz) { + public List createRecords(List dataClearInput) { // List listRecords = new // ArrayList(); @@ -126,7 +135,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { int counter = 0; int record = 0; - List outList = new ArrayList(); + List outList = new ArrayList(); // create CGMS entries (without dates) do { @@ -156,7 +165,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { // System.out.println("Record: " + pe); - outList.add((E)pe); + outList.add(pe); } else { List listRawData = new ArrayList(); listRawData.add((byte)opCode); @@ -174,7 +183,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { // System.out.println("Record: " + pe); - outList.add((E)pe); + outList.add(pe); } } else { CGMSHistoryEntry pe = new CGMSHistoryEntry(); @@ -188,7 +197,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { // System.out.println("Record: " + pe); - outList.add((E)pe); + outList.add(pe); } } while (counter < dataClear.size()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java index 72ae15d020..cb73a73f31 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java @@ -46,7 +46,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; * Author: Andy {andy@atech-software.com} */ -public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { +public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { private static final Logger LOG = LoggerFactory.getLogger(MedtronicPumpHistoryDecoder.class); @@ -64,7 +64,12 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { } - protected List createRecords(List dataClear, Class clazz) { + // @Override + // public Class getHistoryEntryClass() { + // return PumpHistoryEntry.class; + // } + + public List createRecords(List dataClear) { prepareStatistics(); int counter = 0; @@ -72,7 +77,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { boolean incompletePacket = false; deviceType = MedtronicUtil.getMedtronicPumpModel(); - List outList = new ArrayList(); + List outList = new ArrayList(); String skipped = null; int elementStart = 0; @@ -193,7 +198,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { if (decoded == RecordDecodeStatus.OK) // we add only OK records, all others are ignored { - outList.add((E)pe); + outList.add(pe); } } @@ -203,7 +208,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { } - public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn) { + public RecordDecodeStatus decodeRecord(PumpHistoryEntry entryIn) { PumpHistoryEntry precord = (PumpHistoryEntry)entryIn; try { return decodeRecord(entryIn, false); @@ -214,19 +219,6 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { } - public List getTypedList(List list, - Class clazz) { - - List listOut = new ArrayList<>(); - - for (MedtronicHistoryEntry medtronicHistoryEntry : list) { - listOut.add((E)medtronicHistoryEntry); - } - - return listOut; - } - - public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn, boolean x) { // FIXME // TODO @@ -245,6 +237,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { case DailyTotals522: case DailyTotals523: case DailyTotals515: + case EndResultTotals: return decodeDailyTotals(entry); // Not supported at the moment case ChangeBasalPattern: @@ -380,9 +373,9 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { decodeBolus(entry); return RecordDecodeStatus.OK; - case EndResultTotals: - decodeEndResultTotals(entry); - return RecordDecodeStatus.OK; + // case EndResultTotals: + // decodeEndResultTotals(entry); + // return RecordDecodeStatus.OK; case BatteryChange: decodeBatteryActivity(entry); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java index 597aac6eeb..d07e7e2356 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java @@ -137,7 +137,7 @@ public class MedtronicUIPostprocessor { Duration dur = new Duration(clockDTO.localDeviceTime.toDateTime(DateTimeZone.UTC), clockDTO.pumpTime.toDateTime(DateTimeZone.UTC)); - clockDTO.timeDifference = dur.getStandardSeconds(); + clockDTO.timeDifference = (int)dur.getStandardSeconds(); MedtronicUtil.setPumpTime(clockDTO); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 3d8db1d3ac..5b63f5af7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -7,6 +7,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.joda.time.LocalDateTime; +import org.joda.time.Minutes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,10 +24,10 @@ import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpH import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO; import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; //import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; @@ -49,6 +51,7 @@ public class MedtronicHistoryData { private int basalProfileChangedInternally = 0; private Gson gsonPretty; + private List fakeTBRs; public MedtronicHistoryData() { @@ -120,7 +123,7 @@ public class MedtronicHistoryData { } - TBRs = processTBRs(TBRs); + TBRs = preProcessTBRs(TBRs); newHistory2.addAll(TBRs); @@ -204,9 +207,6 @@ public class MedtronicHistoryData { // TODO This logic might not be working correctly public boolean isPumpSuspended(Boolean wasPumpSuspended) { - // if (true) - // return false; - List newAndAll = new ArrayList<>(); if (!isCollectionEmpty(this.allHistory)) { @@ -222,6 +222,8 @@ public class MedtronicHistoryData { this.sort(newAndAll); + List newAndAll2 = filterPumpSuspend(newAndAll); + List items = getFilteredItems(newAndAll, // PumpHistoryEntryType.Bolus, // PumpHistoryEntryType.TempBasalCombined, // @@ -244,83 +246,32 @@ public class MedtronicHistoryData { pumpHistoryEntryType == PumpHistoryEntryType.PumpResume || // pumpHistoryEntryType == PumpHistoryEntryType.Prime); - // if (wasPumpSuspended == null) { // suspension status not known - // - // List items = getFilteredItems(PumpHistoryEntryType.Bolus, // - // PumpHistoryEntryType.TempBasalCombined, // - // PumpHistoryEntryType.Prime, // - // PumpHistoryEntryType.PumpSuspend, // - // PumpHistoryEntryType.PumpResume, // - // PumpHistoryEntryType.Rewind, // - // PumpHistoryEntryType.NoDeliveryAlarm, // - // PumpHistoryEntryType.BasalProfileStart); - // - // if (items.size() == 0) - // return wasPumpSuspended == null ? false : wasPumpSuspended; - // - // PumpHistoryEntry pumpHistoryEntry = items.get(0); - // - // return !(pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.TempBasalCombined || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.BasalProfileStart || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.Bolus || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.PumpResume); - // - // } else { - // - // List items = getFilteredItems(PumpHistoryEntryType.Bolus, // - // PumpHistoryEntryType.TempBasalCombined, // - // PumpHistoryEntryType.Prime, // - // PumpHistoryEntryType.PumpSuspend, // - // PumpHistoryEntryType.PumpResume, // - // PumpHistoryEntryType.Rewind, // - // PumpHistoryEntryType.NoDeliveryAlarm, // - // PumpHistoryEntryType.BasalProfileStart); - // - // if (wasPumpSuspended) { - // - // if (items.size() == 0) - // return wasPumpSuspended == null ? false : wasPumpSuspended; - // - // PumpHistoryEntry pumpHistoryEntry = items.get(0); - // - // if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.TempBasalCombined || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.BasalProfileStart || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.Bolus || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.PumpResume) - // return false; - // else - // return true; - // - // } else { - // - // if (items.size() == 0) - // return wasPumpSuspended == null ? false : wasPumpSuspended; - // - // PumpHistoryEntry pumpHistoryEntry = items.get(0); - // - // if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.NoDeliveryAlarm || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.PumpSuspend || // - // pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.Prime) - // return true; - // - // } - // - // } + } - // FIXME - // return false; + private List filterPumpSuspend(List newAndAll) { + + if (newAndAll.size() < 11) { + return newAndAll; + } + + List newAndAllOut = new ArrayList<>(); + + for (int i = 0; i < 10; i++) { + newAndAllOut.add(newAndAll.get(i)); + } + + return newAndAllOut; } /** * Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime) */ - public void processNewHistoryData() { // TDD - List tdds = getFilteredListByLastRecord(getTDDType()); + List tdds = getFilteredListByLastRecord(PumpHistoryEntryType.EndResultTotals, getTDDType()); LOG.debug("ProcessHistoryData: TDD [count={}, items={}]", tdds.size(), gsonPretty.toJson(tdds)); @@ -331,11 +282,11 @@ public class MedtronicHistoryData { // Bolus List treatments = getFilteredListByLastRecord(PumpHistoryEntryType.Bolus); - LOG.debug("ProcessHistoryData: Bolus [count={}, items=", treatments.size()); + LOG.debug("ProcessHistoryData: Bolus [count={}, itemsCount={}]", treatments.size()); showLogs(null, gsonPretty.toJson(treatments)); if (treatments.size() > 0) { - // processTreatments(treatments); + // processBoluses(treatments); } // TBR @@ -345,14 +296,28 @@ public class MedtronicHistoryData { showLogs(null, gsonPretty.toJson(tbrs)); if (tbrs.size() > 0) { - // processTreatments(treatments); + // processTBRs(tbrs); } - // Fake TBR + // Suspends (for suspends/resume, fakeTBR) + List suspends = getSuspends(); + + LOG.debug("ProcessHistoryData: FakeTBRs (suspend/resume) [count={}, items=", suspends.size()); + showLogs(null, gsonPretty.toJson(suspends)); + + if (suspends.size() > 0) { + // processSuspends(treatments); + } } + ClockDTO pumpTime; - public void processTDDs(List tdds) { + + public void processTDDs(List tddsIn) { + + List tdds = filterTDDs(tddsIn); + + pumpTime = MedtronicUtil.getPumpTime(); LOG.error(getLogPrefix() + "TDDs found: {}. Not processed.\n{}", tdds.size(), gsonPretty.toJson(tdds)); @@ -390,6 +355,66 @@ public class MedtronicHistoryData { } + // TODO needs to be implemented + public void processBoluses(List boluses) { + + int dateDifference = getOldestDateDifference(boluses); + + // List treatmentsFromHistory = TreatmentsPlugin.getPlugin().getTreatmentsFromHistoryXMinutesAgo( + // dateDifference); + + for (PumpHistoryEntry treatment : boluses) { + + LOG.debug("TOE. Treatment: " + treatment); + long inLocalTime = tryToGetByLocalTime(treatment.atechDateTime); + + } + } + + + // TODO needs to be implemented + public void processTBRs(List treatments) { + + int dateDifference = getOldestDateDifference(treatments); + + // List treatmentsFromHistory = TreatmentsPlugin.getPlugin().getTreatmentsFromHistoryXMinutesAgo( + // dateDifference); + + for (PumpHistoryEntry treatment : treatments) { + + LOG.debug("TOE. Treatment: " + treatment); + long inLocalTime = tryToGetByLocalTime(treatment.atechDateTime); + + } + + } + + + // TODO needs to be implemented + public void processSuspends(List treatments) { + + } + + + // TODO needs to be implemented + public List getSuspends() { + return new ArrayList<>(); + } + + + private List filterTDDs(List tdds) { + List tddsOut = new ArrayList<>(); + + for (PumpHistoryEntry tdd : tdds) { + if (tdd.getEntryType() != PumpHistoryEntryType.EndResultTotals) { + tddsOut.add(tdd); + } + } + + return tddsOut.size() == 0 ? tdds : tddsOut; + } + + private TDD findTDD(long atechDateTime, List tddsDb) { for (TDD tdd : tddsDb) { @@ -403,9 +428,42 @@ public class MedtronicHistoryData { } - private void processTreatments(List treatments) { + private long tryToGetByLocalTime(long atechDateTime) { - TreatmentsPlugin.getPlugin().getTreatmentsFromHistory(); + LocalDateTime ldt = DateTimeUtil.toLocalDateTime(atechDateTime); + + LOG.debug("TOE. Time of Entry: " + atechDateTime); + LOG.debug("TOE. Clock Pump: " + pumpTime.pumpTime.toString("HH:mm:ss")); + LOG.debug("TOE. LocalTime: " + pumpTime.localDeviceTime.toString("HH:mm:ss")); + LOG.debug("TOE. Difference(s): " + pumpTime.timeDifference); + + ldt.plusSeconds(pumpTime.timeDifference); + + LOG.debug("TOE. New Time Of Entry: " + ldt.toString("HH:mm:ss")); + + return ldt.toDate().getTime(); + + // return 0; + } + + + private int getOldestDateDifference(List treatments) { + + long dt = Long.MAX_VALUE; + + for (PumpHistoryEntry treatment : treatments) { + + if (treatment.atechDateTime < dt) { + dt = treatment.atechDateTime; + } + } + + LocalDateTime d = DateTimeUtil.toLocalDateTime(dt); + d.minusMinutes(5); + + Minutes minutes = Minutes.minutesBetween(d, new LocalDateTime()); + + return minutes.getMinutes(); } @@ -422,12 +480,6 @@ public class MedtronicHistoryData { // // } - // public List getTDDs() { - // - // return getFilteredListByLastRecord(getTDDType()); - // - // } - private PumpHistoryEntryType getTDDType() { switch (MedtronicUtil.getMedtronicPumpModel()) { @@ -454,15 +506,6 @@ public class MedtronicHistoryData { } - public List getTreatments() { - - return getFilteredListByLastRecord( // - PumpHistoryEntryType.Bolus, // - PumpHistoryEntryType.TempBasalCombined); - - } - - /* * entryType == PumpHistoryEntryType.Bolus || // Treatments * entryType == PumpHistoryEntryType.TempBasalRate || // @@ -589,7 +632,7 @@ public class MedtronicHistoryData { } - private List processTBRs(List TBRs_Input) { + private List preProcessTBRs(List TBRs_Input) { List TBRs = new ArrayList<>(); Map map = new HashMap<>(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java index 60cb31abfe..12d123c361 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java @@ -12,5 +12,7 @@ public class ClockDTO { public LocalDateTime pumpTime; - public long timeDifference; // s + //public Duration timeDifference; + + public int timeDifference; // s } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index e176553a6a..197c178b7b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -1,18 +1,18 @@ package info.nightscout.androidaps.plugins.treatments; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import android.content.Intent; import android.support.annotation.Nullable; import com.crashlytics.android.answers.CustomEvent; import com.squareup.otto.Subscribe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -41,16 +41,16 @@ import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; @@ -58,10 +58,12 @@ import info.nightscout.androidaps.utils.T; * Created by mike on 05.08.2016. */ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface { + private Logger log = LoggerFactory.getLogger(L.DATATREATMENTS); private static TreatmentsPlugin treatmentsPlugin; + public static TreatmentsPlugin getPlugin() { if (treatmentsPlugin == null) treatmentsPlugin = new TreatmentsPlugin(); @@ -79,18 +81,15 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface private final Intervals tempTargets = new OverlappingIntervals<>(); private final ProfileIntervals profiles = new ProfileIntervals<>(); + public TreatmentsPlugin() { - super(new PluginDescription() - .mainType(PluginType.TREATMENT) - .fragmentClass(TreatmentsFragment.class.getName()) - .pluginName(R.string.treatments) - .shortName(R.string.treatments_shortname) - .alwaysEnabled(true) - .description(R.string.description_treatments) - ); + super(new PluginDescription().mainType(PluginType.TREATMENT).fragmentClass(TreatmentsFragment.class.getName()) + .pluginName(R.string.treatments).shortName(R.string.treatments_shortname).alwaysEnabled(true) + .description(R.string.description_treatments)); this.service = new TreatmentService(); } + @Override protected void onStart() { MainApp.bus().register(this); @@ -102,35 +101,39 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface super.onStart(); } + @Override protected void onStop() { MainApp.bus().register(this); } + public TreatmentService getService() { return this.service; } + private void initializeTreatmentData() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTreatmentData"); double dia = Constants.defaultDIA; if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); + long fromMills = (long)(System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); synchronized (treatments) { treatments.clear(); treatments.addAll(getService().getTreatmentDataFromTime(fromMills, false)); } } + private void initializeTempBasalData() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempBasalData"); double dia = Constants.defaultDIA; if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); + long fromMills = (long)(System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); synchronized (tempBasals) { tempBasals.reset().add(MainApp.getDbHelper().getTemporaryBasalsDataFromTime(fromMills, false)); @@ -138,13 +141,14 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } + private void initializeExtendedBolusData() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeExtendedBolusData"); double dia = Constants.defaultDIA; if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); + long fromMills = (long)(System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); synchronized (extendedBoluses) { extendedBoluses.reset().add(MainApp.getDbHelper().getExtendedBolusDataFromTime(fromMills, false)); @@ -152,6 +156,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } + private void initializeTempTargetData() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempTargetData"); @@ -161,6 +166,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + private void initializeProfileSwitchData() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeProfileSwitchData"); @@ -169,11 +175,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public IobTotal getLastCalculationTreatments() { return lastTreatmentCalculation; } + @Override public IobTotal getCalculationToTimeTreatments(long time) { IobTotal total = new IobTotal(time); @@ -184,15 +192,17 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); if (insulinInterface == null) - return total; + return total; double dia = profile.getDia(); synchronized (treatments) { for (Integer pos = 0; pos < treatments.size(); pos++) { Treatment t = treatments.get(pos); - if (!t.isValid) continue; - if (t.date > time) continue; + if (!t.isValid) + continue; + if (t.date > time) + continue; Iob tIOB = t.iobCalc(time, dia); total.iob += tIOB.iobContrib; total.activity += tIOB.activityContrib; @@ -202,7 +212,9 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface // instead of dividing the DIA that only worked on the bilinear curves, // multiply the time the treatment is seen active. long timeSinceTreatment = time - t.date; - long snoozeTime = t.date + (long) (timeSinceTreatment * SP.getDouble(R.string.key_openapsama_bolussnooze_dia_divisor, 2.0)); + long snoozeTime = t.date + + (long)(timeSinceTreatment * SP + .getDouble(R.string.key_openapsama_bolussnooze_dia_divisor, 2.0)); Iob bIOB = t.iobCalc(snoozeTime, dia); total.bolussnooze += bIOB.iobContrib; } @@ -213,7 +225,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface synchronized (extendedBoluses) { for (Integer pos = 0; pos < extendedBoluses.size(); pos++) { ExtendedBolus e = extendedBoluses.get(pos); - if (e.date > time) continue; + if (e.date > time) + continue; IobTotal calc = e.iobCalc(time); total.plus(calc); } @@ -221,23 +234,27 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return total; } + @Override public void updateTotalIOBTreatments() { lastTreatmentCalculation = getCalculationToTimeTreatments(System.currentTimeMillis()); } + @Override public MealData getMealData() { MealData result = new MealData(); Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) return result; + if (profile == null) + return result; long now = System.currentTimeMillis(); long dia_ago = now - (Double.valueOf(profile.getDia() * T.hours(1).msecs())).longValue(); double maxAbsorptionHours = Constants.DEFAULT_MAX_ABSORPTION_TIME; - if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) { + if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) + || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) { maxAbsorptionHours = SP.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME); } else { maxAbsorptionHours = SP.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME); @@ -259,7 +276,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface if (t > absorptionTime_ago && t <= now) { if (treatment.carbs >= 1) { result.carbs += treatment.carbs; - if(t > result.lastCarbTime) + if (t > result.lastCarbTime) result.lastCarbTime = t; } } @@ -277,6 +294,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return result; } + @Override public List getTreatmentsFromHistory() { synchronized (treatments) { @@ -284,6 +302,24 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + + @Override + public List getTreatmentsFromHistoryXMinutesAgo(int minutesAgo) { + List in5minback = new ArrayList<>(); + long time = System.currentTimeMillis(); + synchronized (treatments) { + for (Integer pos = 0; pos < treatments.size(); pos++) { + Treatment t = treatments.get(pos); + if (!t.isValid) + continue; + if (t.date <= time && t.date > (time - minutesAgo * 60 * 1000)) + in5minback.add(t); + } + return in5minback; + } + } + + @Override public List getTreatments5MinBackFromHistory(long time) { List in5minback = new ArrayList<>(); @@ -299,6 +335,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public long getLastBolusTime() { long now = System.currentTimeMillis(); @@ -312,15 +349,17 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } if (L.isEnabled(L.DATATREATMENTS)) - log.debug("Last bolus time: " + new Date(last).toLocaleString()); + log.debug("Last bolus time: " + new Date(last).toLocaleString()); return last; } + @Override public boolean isInHistoryRealTempBasalInProgress() { return getRealTempBasalFromHistory(System.currentTimeMillis()) != null; } + @Override public TemporaryBasal getRealTempBasalFromHistory(long time) { synchronized (tempBasals) { @@ -328,58 +367,66 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public boolean isTempBasalInProgress() { return getTempBasalFromHistory(System.currentTimeMillis()) != null; } + @Override public boolean isInHistoryExtendedBoluslInProgress() { - return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; //TODO: crosscheck here + return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; // TODO: crosscheck here } + @Subscribe public void onStatusEvent(final EventReloadTreatmentData ev) { if (L.isEnabled(L.DATATREATMENTS)) - log.debug("EventReloadTreatmentData"); + log.debug("EventReloadTreatmentData"); initializeTreatmentData(); initializeExtendedBolusData(); updateTotalIOBTreatments(); MainApp.bus().post(ev.next); } + @Subscribe @SuppressWarnings("unused") public void onStatusEvent(final EventReloadTempBasalData ev) { if (L.isEnabled(L.DATATREATMENTS)) - log.debug("EventReloadTempBasalData"); + log.debug("EventReloadTempBasalData"); initializeTempBasalData(); updateTotalIOBTempBasals(); } + @Override public IobTotal getLastCalculationTempBasals() { return lastTempBasalsCalculation; } + @Override public IobTotal getCalculationToTimeTempBasals(long time, Profile profile) { return getCalculationToTimeTempBasals(time, profile, false, 0); } + public IobTotal getCalculationToTimeTempBasals(long time, Profile profile, boolean truncate, long truncateTime) { IobTotal total = new IobTotal(time); InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); if (insulinInterface == null) - return total; + return total; synchronized (tempBasals) { for (Integer pos = 0; pos < tempBasals.size(); pos++) { TemporaryBasal t = tempBasals.get(pos); - if (t.date > time) continue; + if (t.date > time) + continue; IobTotal calc; - if(truncate && t.end() > truncateTime){ + if (truncate && t.end() > truncateTime) { TemporaryBasal dummyTemp = new TemporaryBasal(); dummyTemp.copyFrom(t); dummyTemp.cutEndTo(truncateTime); @@ -387,7 +434,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } else { calc = t.iobCalc(time, profile); } - //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); + // log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); total.plus(calc); } } @@ -396,9 +443,10 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface synchronized (extendedBoluses) { for (Integer pos = 0; pos < extendedBoluses.size(); pos++) { ExtendedBolus e = extendedBoluses.get(pos); - if (e.date > time) continue; + if (e.date > time) + continue; IobTotal calc; - if(truncate && e.end() > truncateTime){ + if (truncate && e.end() > truncateTime) { ExtendedBolus dummyExt = new ExtendedBolus(); dummyExt.copyFrom(e); dummyExt.cutEndTo(truncateTime); @@ -419,6 +467,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return total; } + @Override public void updateTotalIOBTempBasals() { Profile profile = ProfileFunctions.getInstance().getProfile(); @@ -426,6 +475,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface lastTempBasalsCalculation = getCalculationToTimeTempBasals(DateUtil.now(), profile); } + @Nullable @Override public TemporaryBasal getTempBasalFromHistory(long time) { @@ -438,6 +488,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return null; } + @Override public ExtendedBolus getExtendedBolusFromHistory(long time) { synchronized (extendedBoluses) { @@ -445,9 +496,10 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public boolean addToHistoryExtendedBolus(ExtendedBolus extendedBolus) { - //log.debug("Adding new ExtentedBolus record" + extendedBolus.log()); + // log.debug("Adding new ExtentedBolus record" + extendedBolus.log()); boolean newRecordCreated = MainApp.getDbHelper().createOrUpdate(extendedBolus); if (newRecordCreated) { if (extendedBolus.durationInMinutes == 0) { @@ -463,6 +515,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return newRecordCreated; } + @Override public Intervals getExtendedBolusesFromHistory() { synchronized (extendedBoluses) { @@ -470,6 +523,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public Intervals getTemporaryBasalsFromHistory() { synchronized (tempBasals) { @@ -477,9 +531,10 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public boolean addToHistoryTempBasal(TemporaryBasal tempBasal) { - //log.debug("Adding new TemporaryBasal record" + tempBasal.toString()); + // log.debug("Adding new TemporaryBasal record" + tempBasal.toString()); boolean newRecordCreated = MainApp.getDbHelper().createOrUpdate(tempBasal); if (newRecordCreated) { if (tempBasal.durationInMinutes == 0) @@ -492,6 +547,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return newRecordCreated; } + // return true if new record is created @Override public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate) { @@ -509,16 +565,20 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface treatment.boluscalc = detailedBolusInfo.boluscalc != null ? detailedBolusInfo.boluscalc.toString() : null; TreatmentService.UpdateReturn creatOrUpdateResult = getService().createOrUpdate(treatment); boolean newRecordCreated = creatOrUpdateResult.newRecord; - //log.debug("Adding new Treatment record" + treatment.toString()); + // log.debug("Adding new Treatment record" + treatment.toString()); if (detailedBolusInfo.carbTime != 0) { Treatment carbsTreatment = new Treatment(); carbsTreatment.source = detailedBolusInfo.source; carbsTreatment.pumpId = detailedBolusInfo.pumpId; // but this should never happen - carbsTreatment.date = detailedBolusInfo.date + detailedBolusInfo.carbTime * 60 * 1000L + 1000L; // add 1 sec to make them different records + carbsTreatment.date = detailedBolusInfo.date + detailedBolusInfo.carbTime * 60 * 1000L + 1000L; // add 1 sec + // to make + // them + // different + // records carbsTreatment.carbs = detailedBolusInfo.carbs; carbsTreatment.source = detailedBolusInfo.source; getService().createOrUpdate(carbsTreatment); - //log.debug("Adding new Treatment record" + carbsTreatment); + // log.debug("Adding new Treatment record" + carbsTreatment); } if (newRecordCreated && detailedBolusInfo.isValid) NSUpload.uploadTreatmentRecord(detailedBolusInfo); @@ -526,7 +586,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface if (!allowUpdate && !creatOrUpdateResult.success) { log.error("Treatment could not be added to DB", new Exception()); - String status = String.format(MainApp.gs(R.string.error_adding_treatment_message), treatment.insulin, (int) treatment.carbs, DateUtil.dateAndTimeString(treatment.date)); + String status = String.format(MainApp.gs(R.string.error_adding_treatment_message), treatment.insulin, + (int)treatment.carbs, DateUtil.dateAndTimeString(treatment.date)); Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); i.putExtra("soundid", R.raw.error); @@ -543,6 +604,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return newRecordCreated; } + @Override public long oldestDataAvailable() { long oldestTime = System.currentTimeMillis(); @@ -562,6 +624,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return oldestTime; } + // TempTargets @Subscribe @SuppressWarnings("unused") @@ -569,6 +632,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface initializeTempTargetData(); } + @Nullable @Override public TempTarget getTempTargetFromHistory() { @@ -577,6 +641,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Nullable @Override public TempTarget getTempTargetFromHistory(long time) { @@ -585,6 +650,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public Intervals getTempTargetsFromHistory() { synchronized (tempTargets) { @@ -592,13 +658,15 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public void addToHistoryTempTarget(TempTarget tempTarget) { - //log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); + // log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); MainApp.getDbHelper().createOrUpdate(tempTarget); NSUpload.uploadTempTarget(tempTarget); } + // Profile Switch @Subscribe @SuppressWarnings("unused") @@ -606,13 +674,15 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface initializeProfileSwitchData(); } + @Override public ProfileSwitch getProfileSwitchFromHistory(long time) { synchronized (profiles) { - return (ProfileSwitch) profiles.getValueToTime(time); + return (ProfileSwitch)profiles.getValueToTime(time); } } + @Override public ProfileIntervals getProfileSwitchesFromHistory() { synchronized (profiles) { @@ -620,13 +690,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + @Override public void addToHistoryProfileSwitch(ProfileSwitch profileSwitch) { - //log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); + // log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_SWITCH_MISSING)); MainApp.getDbHelper().createOrUpdate(profileSwitch); NSUpload.uploadProfileSwitch(profileSwitch); } - } diff --git a/app/src/main/res/values/wear.xml b/app/src/main/res/values/wear.xml new file mode 100644 index 0000000000..41df8ef193 --- /dev/null +++ b/app/src/main/res/values/wear.xml @@ -0,0 +1,18 @@ + + + + + phone_app_sync_bgs + + \ No newline at end of file diff --git a/app/src/test/java/android/util/Log.java b/app/src/test/java/android/util/Log.java new file mode 100644 index 0000000000..bc2777f297 --- /dev/null +++ b/app/src/test/java/android/util/Log.java @@ -0,0 +1,59 @@ +package android.util; + +import java.time.LocalDateTime; + +/** + * Created by andy on 3/10/19. + */ + +public class Log { + + // 03-10 13:44:42.847 12790-12888/info.nightscout.androidaps D/MedtronicHistoryData: + + static boolean isLoggingEnabled = false; + + + public static void setLoggingEnabled(boolean enabled) { + isLoggingEnabled = enabled; + } + + + private void writeLog(String type, String tag, String message) { + if (isLoggingEnabled) { + LocalDateTime ldt = LocalDateTime.now(); + System.out.println("DEBUG: " + tag + ": " + message); + } + } + + + public static int d(String tag, String msg) { + System.out.println("DEBUG: " + tag + ": " + msg); + return 0; + } + + + public static int v(String tag, String msg) { + System.out.println("VERBOSE: " + tag + ": " + msg); + return 0; + } + + + public static int i(String tag, String msg) { + System.out.println("INFO: " + tag + ": " + msg); + return 0; + } + + + public static int w(String tag, String msg) { + System.out.println("WARN: " + tag + ": " + msg); + return 0; + } + + + public static int e(String tag, String msg) { + System.out.println("ERROR: " + tag + ": " + msg); + return 0; + } + + // add other methods if required... +} diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicHistoryDataUTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicHistoryDataUTest.java new file mode 100644 index 0000000000..9a9c5d5402 --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicHistoryDataUTest.java @@ -0,0 +1,75 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import uk.org.lidalia.slf4jtest.TestLogger; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + +import android.util.Log; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; + +/** + * Created by andy on 3/10/19. + */ + +public class MedtronicHistoryDataUTest { + + TestLogger LOGGER = TestLoggerFactory.getTestLogger(MedtronicHistoryDataUTest.class); + + byte[] historyPageData = ByteUtil + .createByteArrayFromString("16 00 12 EC 14 47 13 33 00 14 F2 14 47 13 00 16 01 14 F2 14 47 13 33 00 1C C9 15 47 13 00 16 00 1C C9 15 47 13 33 4E 31 D3 15 47 13 00 16 01 31 D3 15 47 13 33 00 1A F1 15 47 13 00 16 00 1A F1 15 47 13 33 50 1D F1 15 47 13 00 16 01 1D F1 15 47 13 33 50 11 D8 16 47 13 00 16 01 11 D8 16 47 13 33 50 31 FB 16 47 13 00 16 01 31 FB 16 47 13 33 50 12 E3 17 47 13 00 16 01 12 E3 17 47 13 33 00 1E FB 17 47 13 00 16 00 1E FB 17 47 13 33 D8 21 FB 17 47 13 00 16 01 21 FB 17 47 13 07 00 00 05 CC 27 93 6D 27 93 05 0C 00 E8 00 00 00 00 05 CC 05 CC 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00 E8 00 00 00 33 00 36 C4 00 48 13 00 16 00 36 C4 00 48 13 33 D8 29 C9 00 48 13 00 16 01 29 C9 00 48 13 33 00 12 E7 00 48 13 00 16 00 12 E7 00 48 13 33 BC 19 C9 01 48 13 00 16 01 19 C9 01 48 13 33 00 26 CE 01 48 13 00 16 00 26 CE 01 48 13 33 44 29 CE 01 48 13 00 16 01 29 CE 01 48 13 33 00 13 D3 01 48 13 00 16 00 13 D3 01 48 13 33 64 31 F1 01 48 13 00 16 01 31 F1 01 48 13 33 00 0B F7 01 48 13 00 16 00 0B F7 01 48 13 33 00 12 D8 02 48 13 00 16 01 12 D8 02 48 13 33 00 10 F1 02 48 13 00 16 00 10 F1 02 48 13 33 00 30 C4 03 48 13 00 16 01 30 C4 03 48 13 33 00 04 CA 03 48 13 00 16 00 04 CA 03 48 13 33 00 2F D3 03 48 13 00 16 01 2F D3 03 48 13 33 00 30 D8 03 48 13 00 16 00 30 D8 03 48 13 33 00 13 E7 03 48 13 00 16 01 13 E7 03 48 13 33 00 2E FB 03 48 13 00 16 00 2E FB 03 48 13 19 00 00 C1 04 08 13 07 00 00 04 0C 28 93 6D 28 93 05 0C 00 E8 00 00 00 00 04 0C 04 0C 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00 E8 00 00 00 06 3E 03 7A 19 DC 48 49 13 0C 3E 0C E6 08 09 13 07 00 00 01 E4 29 93 6D 29 93 05 0C 00 E8 00 00 00 00 01 E4 01 E4 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00 E8 00 00 00 1A 00 13 D2 0D 0A 13 1A 01 28 D2 0D 0A 13 21 00 2A D8 0D 0A 13 03 00 00 00 0E 2D D9 2D 0A 13 33 98 26 DE 0D 4A 13 00 16 01 26 DE 0D 4A 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5D 70"); + + MedtronicPumpHistoryDecoder decoder = new MedtronicPumpHistoryDecoder(); + + + // Logger LOGGER = LoggerFactory.getLogger(MedtronicHistoryDataUTest.class); + + @Before + public void setup() { + + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace"); + + // final TestAppender appender = new TestAppender(); + // final Logger logger = Logger.getRootLogger(); + // logger.addAppender(appender); + // try { + // Logger.getLogger(MyTest.class).info("Test"); + // } finally { + // logger.removeAppender(appender); + // } + } + + + @Test + public void testTBR() throws Exception { + + RawHistoryPage historyPage = new RawHistoryPage(); + historyPage.appendData(historyPageData); + + List pumpHistoryEntries = decoder.processPageAndCreateRecords(historyPage, + PumpHistoryEntry.class); + + System.out.println("PumpHistoryEntries: " + pumpHistoryEntries.size()); + + Log.d("Test", "Log.d"); + LOGGER.debug("Logger.debug"); + + for (PumpHistoryEntry pumpHistoryEntry : pumpHistoryEntries) { + Log.d("MedtronicHistoryDataUTest", pumpHistoryEntry.toString()); + } + + } + + + public void testBolus() throws Exception { + + } + +} diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java index a1568ed42d..e8ab39a898 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java @@ -1,7 +1,6 @@ package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; import org.junit.Before; -import org.junit.Test; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; @@ -90,7 +89,7 @@ public class MedtronicPumpHistoryDecoderUTest { } - @Test + // @Test public void decodeDailyTotals515() { byte[] data = ByteUtil @@ -111,7 +110,7 @@ public class MedtronicPumpHistoryDecoderUTest { } - @Test + // @Test public void decodeDailyTotals523() { byte[] data = new byte[] { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileUTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileUTest.java index e71e414042..5431d09f49 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileUTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileUTest.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; +import static org.mockito.Mockito.when; + import junit.framework.Assert; import org.junit.Before; @@ -14,12 +16,12 @@ import info.SPMocker; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.utils.DateUtil; -import info.nightscout.utils.SP; -import info.nightscout.utils.T; +import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.utils.T; /** * Created by andy on 6/16/18. diff --git a/app/src/test/java/info/nightscout/androidaps/utils/LoggerRule.java b/app/src/test/java/info/nightscout/androidaps/utils/LoggerRule.java new file mode 100644 index 0000000000..0f6adc888e --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/utils/LoggerRule.java @@ -0,0 +1,62 @@ +package info.nightscout.androidaps.utils; + +/** + * Created by andy on 3/10/19. + */ + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +//import ch.qos.logback.core.read.ListAppender; + +public class LoggerRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + return null; + } + + /* + * private final ListAppender listAppender = new ListAppender<>(); + * private final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + * + * + * @Override + * public Statement apply(Statement base, Description description) { + * return new Statement() { + * + * @Override + * public void evaluate() throws Throwable { + * setup(); + * base.evaluate(); + * teardown(); + * } + * }; + * } + * + * + * private void setup() { + * logger.addAppender(listAppender); + * listAppender.start(); + * } + * + * + * private void teardown() { + * listAppender.stop(); + * listAppender.list.clear(); + * logger.detachAppender(listAppender); + * } + * + * + * public List getMessages() { + * return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList()); + * } + * + * + * public List getFormattedMessages() { + * return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList()); + * } + */ + +} diff --git a/wear/build.gradle b/wear/build.gradle index cf846f51ae..fe403f9627 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'com.android.application' ext { wearableVersion = "2.0.1" + playServicesWearable = "10.2.1" } def generateGitBuild = { -> @@ -89,8 +90,8 @@ dependencies { //compile "com.ustwo.android:clockwise-wearable:1.0.2" compileOnly "com.google.android.wearable:wearable:${wearableVersion}" implementation "com.google.android.support:wearable:${wearableVersion}" - implementation "com.google.android.gms:play-services-wearable:7.3.0" - implementation(name:"ustwo-clockwise-debug", ext:"aar") + implementation "com.google.android.gms:play-services-wearable:${playServicesWearable}" + implementation(name: "ustwo-clockwise-debug", ext: "aar") implementation "com.android.support:support-v4:27.0.1" implementation 'com.android.support:wear:27.0.1' implementation "me.denley.wearpreferenceactivity:wearpreferenceactivity:0.5.0" diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index e6750c515d..d371bb5be8 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ - + + - + + + + + + + + + + + + + + + + + + diff --git a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java index 52dfc97ad1..edf7f02dc3 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.data; +import java.util.Set; +import java.util.concurrent.TimeUnit; + import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -10,13 +13,18 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; import android.preference.PreferenceManager; -import android.support.v4.content.LocalBroadcastManager; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; - +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.wearable.CapabilityApi; +import com.google.android.gms.wearable.CapabilityInfo; +import com.google.android.gms.wearable.ChannelApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMap; @@ -26,19 +34,18 @@ import com.google.android.gms.wearable.NodeApi; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; -import java.util.concurrent.TimeUnit; - -import info.nightscout.androidaps.interaction.AAPSPreferences; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interaction.AAPSPreferences; import info.nightscout.androidaps.interaction.actions.AcceptActivity; import info.nightscout.androidaps.interaction.actions.CPPActivity; import info.nightscout.androidaps.interaction.utils.SafeParse; +import info.nightscout.androidaps.interaction.utils.WearUtil; /** * Created by emmablack on 12/26/14. */ -public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener { +public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ChannelApi.ChannelListener { + private static final String WEARABLE_DATA_PATH = "/nightscout_watch_data"; private static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend"; private static final String WEARABLE_CANCELBOLUS_PATH = "/nightscout_watch_cancel_bolus"; @@ -54,7 +61,6 @@ public class ListenerService extends WearableListenerService implements GoogleAp public static final String NEW_CHANGECONFIRMATIONREQUEST_PATH = "/nightscout_watch_changeconfirmationrequest"; public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; - public static final int BOLUS_PROGRESS_NOTIF_ID = 001; public static final int CONFIRM_NOTIF_ID = 002; public static final int CHANGE_NOTIF_ID = 556677; @@ -65,63 +71,181 @@ public class ListenerService extends WearableListenerService implements GoogleAp private static final String ACTION_CONFIRMCHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE"; private static final String ACTION_INITIATE_ACTION = "com.dexdrip.stephenblack.nightwatch.INITIATE_ACTION"; - private static final String ACTION_RESEND_BULK = "com.dexdrip.stephenblack.nightwatch.RESEND_BULK_DATA"; + GoogleApiClient googleApiClient; private long lastRequest = 0; private DismissThread confirmThread; private DismissThread bolusprogressThread; + private static final String TAG = "ListenerService"; + private DataRequester mDataRequester = null; + private static final int GET_CAPABILITIES_TIMEOUT_MS = 5000; + + // Phone + private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs"; + private static final String MESSAGE_PATH_PHONE = "/phone_message_path"; + // Wear + private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs"; + private static final String MESSAGE_PATH_WEAR = "/wear_message_path"; + private String mPhoneNodeId = null; + private String localnode = null; + private String logPrefix = ""; // "WR: " public class DataRequester extends AsyncTask { - Context mContext; - DataRequester(Context context) { - mContext = context; + Context mContext; + String path; + byte[] payload; + + + DataRequester(Context context, String thispath, byte[] thispayload) { + path = thispath; + payload = thispayload; + // Log.d(TAG, logPrefix + "DataRequester DataRequester: " + thispath + " lastRequest:" + lastRequest); } + @Override protected Void doInBackground(Void... params) { - if (googleApiClient.isConnected()) { - if (System.currentTimeMillis() - lastRequest > 20 * 1000) { // enforce 20-second debounce period - lastRequest = System.currentTimeMillis(); + // Log.d(TAG, logPrefix + "DataRequester: doInBack: " + params); - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); - for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_RESEND_PATH, null); - } + try { + + forceGoogleApiConnect(); + DataMap datamap; + + if (isCancelled()) { + Log.d(TAG, "doInBackground CANCELLED programmatically"); + return null; } - } else { - googleApiClient.blockingConnect(15, TimeUnit.SECONDS); - if (googleApiClient.isConnected()) { - if (System.currentTimeMillis() - lastRequest > 20 * 1000) { // enforce 20-second debounce period + + if (googleApiClient != null) { + if (!googleApiClient.isConnected()) + googleApiClient.blockingConnect(15, TimeUnit.SECONDS); + } + + // this code might not be needed in this way, but we need to see that later + if ((googleApiClient != null) && (googleApiClient.isConnected())) { + if ((System.currentTimeMillis() - lastRequest > 20 * 1000)) { + + // enforce 20-second debounce period lastRequest = System.currentTimeMillis(); - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); - for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_RESEND_PATH, null); + // NodeApi.GetConnectedNodesResult nodes = + // Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); + if (localnode == null || (localnode != null && localnode.isEmpty())) + setLocalNodeName(); + + CapabilityInfo capabilityInfo = getCapabilities(); + + int count = 0; + Node phoneNode = null; + + if (capabilityInfo != null) { + phoneNode = updatePhoneSyncBgsCapability(capabilityInfo); + count = capabilityInfo.getNodes().size(); } + + Log.d(TAG, "doInBackground connected. CapabilityApi.GetCapabilityResult mPhoneNodeID=" + + (phoneNode != null ? phoneNode.getId() : "") + " count=" + count + " localnode=" + + localnode);// KS + + if (count > 0) { + + for (Node node : capabilityInfo.getNodes()) { + + // Log.d(TAG, "doInBackground path: " + path); + + switch (path) { + // simple send as is payloads + + case WEARABLE_RESEND_PATH: + Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), + WEARABLE_RESEND_PATH, null); + break; + case WEARABLE_DATA_PATH: + case WEARABLE_CANCELBOLUS_PATH: + case WEARABLE_CONFIRM_ACTIONSTRING_PATH: + case WEARABLE_INITIATE_ACTIONSTRING_PATH: + case OPEN_SETTINGS: + case NEW_STATUS_PATH: + case NEW_PREFERENCES_PATH: + case BASAL_DATA_PATH: + case BOLUS_PROGRESS_PATH: + case ACTION_CONFIRMATION_REQUEST_PATH: + case NEW_CHANGECONFIRMATIONREQUEST_PATH: + case ACTION_CANCELNOTIFICATION_REQUEST_PATH: { + Log.w(TAG, logPrefix + "Unhandled path"); + // sendMessagePayload(node, path, path, payload); + } + + default:// SYNC_ALL_DATA + // this fall through is messy and non-deterministic for new paths + + } + } + } else { + + Log.d(TAG, logPrefix + "doInBackground connected but getConnectedNodes returns 0."); + + } + } else { + // no resend + Log.d(TAG, logPrefix + "Inside the timeout, will not be executed"); + + } + } else { + Log.d(TAG, logPrefix + "Not connected for sending: api " + + ((googleApiClient == null) ? "is NULL!" : "not null")); + if (googleApiClient != null) { + googleApiClient.connect(); + } else { + googleApiConnect(); } } + + } catch (Exception ex) { + Log.e(TAG, logPrefix + "Error executing DataRequester in background. Exception: " + ex.getMessage()); } + return null; } } + + public CapabilityInfo getCapabilities() { + + CapabilityApi.GetCapabilityResult capabilityResult = Wearable.CapabilityApi.getCapability(googleApiClient, + CAPABILITY_PHONE_APP, CapabilityApi.FILTER_REACHABLE).await(GET_CAPABILITIES_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + + if (!capabilityResult.getStatus().isSuccess()) { + Log.e(TAG, logPrefix + "doInBackground Failed to get capabilities, status: " + + capabilityResult.getStatus().getStatusMessage()); + return null; + } + + return capabilityResult.getCapability(); + + } + public class BolusCancelTask extends AsyncTask { + Context mContext; + BolusCancelTask(Context context) { mContext = context; } + @Override protected Void doInBackground(Void... params) { + // Log.d(TAG, logPrefix + "BolusCancelTask: doInBack: " + params); + if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); + NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); for (Node node : nodes.getNodes()) { Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_CANCELBOLUS_PATH, null); } @@ -129,8 +253,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp } else { googleApiClient.blockingConnect(15, TimeUnit.SECONDS); if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); + NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); for (Node node : nodes.getNodes()) { Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_CANCELBOLUS_PATH, null); } @@ -142,32 +265,40 @@ public class ListenerService extends WearableListenerService implements GoogleAp } public class MessageActionTask extends AsyncTask { + Context mContext; String mActionstring; String mMessagePath; + MessageActionTask(Context context, String messagePath, String actionstring) { mContext = context; mActionstring = actionstring; mMessagePath = messagePath; } + @Override protected Void doInBackground(Void... params) { + + // Log.d(TAG, logPrefix + "MessageActionTask: doInBack: " + params); + + forceGoogleApiConnect(); + if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); + NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, mActionstring.getBytes()); + Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, + mActionstring.getBytes()); } } else { googleApiClient.blockingConnect(15, TimeUnit.SECONDS); if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); + NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, mActionstring.getBytes()); + Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, + mActionstring.getBytes()); } } } @@ -175,72 +306,153 @@ public class ListenerService extends WearableListenerService implements GoogleAp } } + public void requestData() { - new DataRequester(this).execute(); + sendData(WEARABLE_RESEND_PATH, null); } + public void cancelBolus() { new BolusCancelTask(this).execute(); } + private void sendConfirmActionstring(String actionstring) { new MessageActionTask(this, WEARABLE_CONFIRM_ACTIONSTRING_PATH, actionstring).execute(); } + private void sendInitiateActionstring(String actionstring) { new MessageActionTask(this, WEARABLE_INITIATE_ACTIONSTRING_PATH, actionstring).execute(); } - public void googleApiConnect() { - googleApiClient = new GoogleApiClient.Builder(this) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .addApi(Wearable.API) - .build(); + + private Node updatePhoneSyncBgsCapability(CapabilityInfo capabilityInfo) { + // Log.d(TAG, "CapabilityInfo: " + capabilityInfo); + + Set connectedNodes = capabilityInfo.getNodes(); + return pickBestNode(connectedNodes); + // mPhoneNodeId = pickBestNodeId(connectedNodes); + } + + + private Node pickBestNode(Set nodes) { + Node bestNode = null; + // Find a nearby node or pick one arbitrarily + for (Node node : nodes) { + if (node.isNearby()) { + return node; + } + bestNode = node; + } + return bestNode; + } + + + private synchronized void sendData(String path, byte[] payload) { + // Log.d(TAG, "WR: sendData: path: " + path + ", payload=" + payload); + + if (path == null) + return; + if (mDataRequester != null) { + // Log.d(TAG, logPrefix + "sendData DataRequester != null lastRequest:" + + // WearUtil.dateTimeText(lastRequest)); + if (mDataRequester.getStatus() != AsyncTask.Status.FINISHED) { + // Log.d(TAG, logPrefix + "sendData Should be canceled? Let run 'til finished."); + // mDataRequester.cancel(true); + } + // mDataRequester = null; + } + + Log.d(TAG, logPrefix + "sendData: execute lastRequest:" + WearUtil.dateTimeText(lastRequest)); + mDataRequester = (DataRequester)new DataRequester(this, path, payload).execute(); + // executeTask(mDataRequester); + + // if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // Log.d(TAG, "sendData SDK < M call execute lastRequest:" + WearUtil.dateTimeText(lastRequest)); + // mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute(); + // } else { + // Log.d(TAG, "sendData SDK >= M call executeOnExecutor lastRequest:" + WearUtil.dateTimeText(lastRequest)); + // // TODO xdrip executor + // mDataRequester = (DataRequester) new DataRequester(this, path, payload).executeOnExecutor(xdrip.executor); + // } + } + + + private void googleApiConnect() { + if (googleApiClient != null) { + // Remove old listener(s) + try { + Wearable.ChannelApi.removeListener(googleApiClient, this); + } catch (Exception e) { + // + } + try { + Wearable.MessageApi.removeListener(googleApiClient, this); + } catch (Exception e) { + // + } + } + + googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this) + .addOnConnectionFailedListener(this).addApi(Wearable.API).build(); Wearable.MessageApi.addListener(googleApiClient, this); } + private void forceGoogleApiConnect() { + if ((googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) + || googleApiClient == null) { + try { + Log.d(TAG, "forceGoogleApiConnect: forcing google api reconnection"); + googleApiConnect(); + Thread.sleep(2000); + } catch (InterruptedException e) { + // + } + } + } + + @Override public int onStartCommand(Intent intent, int flags, int startId) { + + // Log.d(TAG, logPrefix + "onStartCommand: Intent: " + intent); + if (intent != null && ACTION_RESEND.equals(intent.getAction())) { googleApiConnect(); requestData(); - } else if(intent != null && ACTION_CANCELBOLUS.equals(intent.getAction())){ + } else if (intent != null && ACTION_CANCELBOLUS.equals(intent.getAction())) { googleApiConnect(); - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); + // dismiss notification + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(ListenerService.this); notificationManager.cancel(BOLUS_PROGRESS_NOTIF_ID); - //send cancel-request to phone. + // send cancel-request to phone. cancelBolus(); - - } else if(intent != null && ACTION_CONFIRMATION.equals(intent.getAction())){ + } else if (intent != null && ACTION_CONFIRMATION.equals(intent.getAction())) { googleApiConnect(); - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); + // dismiss notification + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(ListenerService.this); notificationManager.cancel(CONFIRM_NOTIF_ID); String actionstring = intent.getStringExtra("actionstring"); sendConfirmActionstring(actionstring); - } else if(intent != null && ACTION_CONFIRMCHANGE.equals(intent.getAction())){ + } else if (intent != null && ACTION_CONFIRMCHANGE.equals(intent.getAction())) { googleApiConnect(); - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); + // dismiss notification + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(ListenerService.this); notificationManager.cancel(CHANGE_NOTIF_ID); String actionstring = intent.getStringExtra("actionstring"); sendConfirmActionstring(actionstring); - } else if(intent != null && ACTION_INITIATE_ACTION.equals(intent.getAction())){ + } else if (intent != null && ACTION_INITIATE_ACTION.equals(intent.getAction())) { googleApiConnect(); String actionstring = intent.getStringExtra("actionstring"); @@ -256,31 +468,37 @@ public class ListenerService extends WearableListenerService implements GoogleAp public void onDataChanged(DataEventBuffer dataEvents) { DataMap dataMap; + // Log.d(TAG, logPrefix + "onDataChanged: DataEvents=" + dataEvents); for (DataEvent event : dataEvents) { if (event.getType() == DataEvent.TYPE_CHANGED) { - String path = event.getDataItem().getUri().getPath(); + + Log.d(TAG, "WR: onDataChanged: Path: " + path + ", EventDataItem=" + event.getDataItem()); + if (path.equals(OPEN_SETTINGS)) { Intent intent = new Intent(this, AAPSPreferences.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } else if (path.equals(BOLUS_PROGRESS_PATH)) { - int progress = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getInt("progresspercent", 0); - String status = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("progressstatus", ""); + int progress = DataMapItem.fromDataItem(event.getDataItem()).getDataMap() + .getInt("progresspercent", 0); + String status = DataMapItem.fromDataItem(event.getDataItem()).getDataMap() + .getString("progressstatus", ""); showBolusProgress(progress, status); } else if (path.equals(ACTION_CONFIRMATION_REQUEST_PATH)) { String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title"); String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message"); - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); + String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap() + .getString("actionstring"); - if("opencpp".equals(title) && actionstring.startsWith("opencpp")){ + if ("opencpp".equals(title) && actionstring.startsWith("opencpp")) { String[] act = actionstring.split("\\s+"); Intent intent = new Intent(this, CPPActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //TODO adrian: parse actionstring and add parameters + // TODO adrian: parse actionstring and add parameters Bundle params = new Bundle(); params.putInt("percentage", SafeParse.stringToInt(act[1])); params.putInt("timeshift", SafeParse.stringToInt(act[2])); @@ -296,15 +514,15 @@ public class ListenerService extends WearableListenerService implements GoogleAp messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("status", dataMap.toBundle()); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); - } else if (path.equals(BASAL_DATA_PATH)){ + } else if (path.equals(BASAL_DATA_PATH)) { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); messageIntent.putExtra("basals", dataMap.toBundle()); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); - } else if (path.equals(NEW_PREFERENCES_PATH)){ + } else if (path.equals(NEW_PREFERENCES_PATH)) { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - if(dataMap.containsKey("wearcontrol")) { + if (dataMap.containsKey("wearcontrol")) { boolean wearcontrol = dataMap.getBoolean("wearcontrol", false); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPreferences.edit(); @@ -314,10 +532,12 @@ public class ListenerService extends WearableListenerService implements GoogleAp } else if (path.equals(NEW_CHANGECONFIRMATIONREQUEST_PATH)) { String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title"); String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message"); - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); + String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap() + .getString("actionstring"); notifyChangeRequest(title, message, actionstring); } else if (path.equals(ACTION_CANCELNOTIFICATION_REQUEST_PATH)) { - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); + String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap() + .getString("actionstring"); cancelNotificationRequest(actionstring); } else { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); @@ -330,14 +550,12 @@ public class ListenerService extends WearableListenerService implements GoogleAp } } + private void notifyChangeRequest(String title, String message, String actionstring) { - Notification.Builder builder = - new Notification.Builder(this); //,"AndroidAPS-Openloop"); - builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(title) - .setContentText(message) - .setPriority(Notification.PRIORITY_HIGH); + Notification.Builder builder = new Notification.Builder(this); // ,"AndroidAPS-Openloop"); + builder.setSmallIcon(R.drawable.notif_icon).setContentTitle(title).setContentText(message) + .setPriority(Notification.PRIORITY_HIGH); // Creates an explicit intent for an Activity in your app Intent intent = new Intent(this, AcceptActivity.class); @@ -348,62 +566,56 @@ public class ListenerService extends WearableListenerService implements GoogleAp params.putString("actionstring", actionstring); intent.putExtras(params); - PendingIntent resultPendingIntent = - PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); - builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); + builder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 }); - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationManager mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(CHANGE_NOTIF_ID, builder.build()); } + private void cancelNotificationRequest(String actionstring) { - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationManager mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); mNotificationManager.cancel(CHANGE_NOTIF_ID); } + private void showBolusProgress(int progresspercent, String progresstatus) { Intent cancelIntent = new Intent(this, ListenerService.class); cancelIntent.setAction(ACTION_CANCELBOLUS); PendingIntent cancelPendingIntent = PendingIntent.getService(this, 0, cancelIntent, 0);; long[] vibratePattern; - boolean vibreate = PreferenceManager - .getDefaultSharedPreferences(this).getBoolean("vibrateOnBolus", true); - if(vibreate){ - vibratePattern = new long[]{0, 50, 1000}; + boolean vibreate = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("vibrateOnBolus", true); + if (vibreate) { + vibratePattern = new long[] { 0, 50, 1000 }; } else { - vibratePattern = new long[]{0, 1, 1000}; + vibratePattern = new long[] { 0, 1, 1000 }; } - NotificationCompat.Builder notificationBuilder = - new NotificationCompat.Builder(this) - .setSmallIcon(R.drawable.ic_icon) - .setContentTitle("Bolus Progress") - .setContentText(progresspercent + "% - " + progresstatus) - .setContentIntent(cancelPendingIntent) - .setPriority(NotificationCompat.PRIORITY_MAX) - .setVibrate(vibratePattern) - .addAction(R.drawable.ic_cancel, "CANCEL BOLUS", cancelPendingIntent); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_icon).setContentTitle("Bolus Progress") + .setContentText(progresspercent + "% - " + progresstatus).setContentIntent(cancelPendingIntent) + .setPriority(NotificationCompat.PRIORITY_MAX).setVibrate(vibratePattern) + .addAction(R.drawable.ic_cancel, "CANCEL BOLUS", cancelPendingIntent); - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(this); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); - if(confirmThread != null){ + if (confirmThread != null) { confirmThread.invalidate(); } notificationManager.notify(BOLUS_PROGRESS_NOTIF_ID, notificationBuilder.build()); notificationManager.cancel(CONFIRM_NOTIF_ID); // multiple watch setup - - if (progresspercent == 100){ + if (progresspercent == 100) { scheduleDismissBolusprogress(5); } } + private void showConfirmationDialog(String title, String message, String actionstring) { Intent intent = new Intent(this, AcceptActivity.class); @@ -416,49 +628,54 @@ public class ListenerService extends WearableListenerService implements GoogleAp startActivity(intent); } + private void scheduleDismissBolusprogress(final int seconds) { - if(confirmThread != null){ + if (confirmThread != null) { confirmThread.invalidate(); } bolusprogressThread = new DismissThread(BOLUS_PROGRESS_NOTIF_ID, seconds); bolusprogressThread.start(); } + private class DismissThread extends Thread { - - private class DismissThread extends Thread{ private final int notificationID; private final int seconds; private boolean valid = true; - DismissThread(int notificationID, int seconds){ + + DismissThread(int notificationID, int seconds) { this.notificationID = notificationID; this.seconds = seconds; } - public synchronized void invalidate(){ + + public synchronized void invalidate() { valid = false; } + @Override public void run() { SystemClock.sleep(seconds * 1000); synchronized (this) { - if(valid) { - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); + if (valid) { + NotificationManagerCompat notificationManager = NotificationManagerCompat + .from(ListenerService.this); notificationManager.cancel(notificationID); } } } } + public static void requestData(Context context) { Intent intent = new Intent(context, ListenerService.class); intent.setAction(ACTION_RESEND); context.startService(intent); } + public static void initiateAction(Context context, String actionstring) { Intent intent = new Intent(context, ListenerService.class); intent.putExtra("actionstring", actionstring); @@ -466,6 +683,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp context.startService(intent); } + public static void confirmAction(Context context, String actionstring) { Intent intent = new Intent(context, ListenerService.class); intent.putExtra("actionstring", actionstring); @@ -478,29 +696,71 @@ public class ListenerService extends WearableListenerService implements GoogleAp context.startService(intent); } + @Override public void onConnected(Bundle bundle) { + // Log.d(TAG, logPrefix + "onConnected call requestData"); + + CapabilityApi.CapabilityListener capabilityListener = new CapabilityApi.CapabilityListener() { + + @Override + public void onCapabilityChanged(CapabilityInfo capabilityInfo) { + updatePhoneSyncBgsCapability(capabilityInfo); + Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mPhoneNodeID:" + mPhoneNodeId + + ", Capability: " + capabilityInfo); + } + }; + + Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_PHONE_APP); + + Wearable.ChannelApi.addListener(googleApiClient, this); requestData(); } + @Override public void onConnectionSuspended(int i) { } + @Override public void onConnectionFailed(ConnectionResult connectionResult) { } + + private void setLocalNodeName() { + forceGoogleApiConnect(); + PendingResult result = Wearable.NodeApi.getLocalNode(googleApiClient); + result.setResultCallback(new ResultCallback() { + + @Override + public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) { + if (!getLocalNodeResult.getStatus().isSuccess()) { + Log.e(TAG, "ERROR: failed to getLocalNode Status=" + + getLocalNodeResult.getStatus().getStatusMessage()); + } else { + Log.d(TAG, "getLocalNode Status=: " + getLocalNodeResult.getStatus().getStatusMessage()); + Node getnode = getLocalNodeResult.getNode(); + localnode = getnode != null ? getnode.getDisplayName() + "|" + getnode.getId() : ""; + Log.d(TAG, "setLocalNodeName. localnode=" + localnode); + } + } + }); + } + + @Override public void onDestroy() { super.onDestroy(); if (googleApiClient != null && googleApiClient.isConnected()) { googleApiClient.disconnect(); } + if (googleApiClient != null) { Wearable.MessageApi.removeListener(googleApiClient, this); + Wearable.ChannelApi.removeListener(googleApiClient, this); } } } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java new file mode 100644 index 0000000000..bd9297b96e --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/WearUtil.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.interaction.utils; + +import java.util.Date; + +/** + * Created by andy on 3/5/19. + */ + +public class WearUtil { + + public static String dateTimeText(long timeInMs) { + Date d = new Date(timeInMs); + return "" + d.getDay() + "." + d.getMonth() + "." + d.getYear() + " " + d.getHours() + ":" + d.getMinutes() + + ":" + d.getSeconds(); + } + +} diff --git a/wear/src/main/res/values/wear.xml b/wear/src/main/res/values/wear.xml new file mode 100644 index 0000000000..41df8ef193 --- /dev/null +++ b/wear/src/main/res/values/wear.xml @@ -0,0 +1,18 @@ + + + + + phone_app_sync_bgs + + \ No newline at end of file