diff --git a/app/build.gradle b/app/build.gradle index fdeaf0d8f4fe5050ed0397010e641fa2c4cf5c7c..046b9fced71dfc9fcedd0a2b855301b59742d26f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ import groovy.json.JsonSlurper buildscript { repositories { maven { url 'https://maven.fabric.io/public' } + maven { url "https://jitpack.io" } jcenter() } @@ -49,7 +50,7 @@ def generateVersionName() { def getBugfenderApiKey() { String bugfenderApiKey = System.getenv("BUGFENDER_API_KEY") - if(bugfenderApiKey == null) { + if (bugfenderApiKey == null) { File file = new File("app/bugfender.properties") if (file.exists()) { Properties properties = new Properties() @@ -65,8 +66,8 @@ def getBugfenderApiKey() { } android { - compileSdkVersion 27 - buildToolsVersion '28.0.2' + compileSdkVersion 28 + buildToolsVersion '28.0.3' /* compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -80,15 +81,17 @@ android { defaultConfig { applicationId "info.nightscout.android" minSdkVersion 14 - targetSdkVersion 27 + targetSdkVersion 28 versionName generateVersionName() - versionCode generateVersionCode() // FIXME - this forces a need for an online lookup and compile fails when offline + versionCode generateVersionCode() + // FIXME - this forces a need for an online lookup and compile fails when offline buildConfigField "String", "BUGFENDER_API_KEY", getBugfenderApiKey() } buildTypes { release { - minifyEnabled false // Set to true once we set up proguard-rules without breaking anything + minifyEnabled false + // Set to true once we set up proguard-rules without breaking anything zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' ext.enableCrashlytics = file("fabric.properties").exists() @@ -125,14 +128,17 @@ dependencies { implementation files('libs/lzo-core-1.0.5.jar') implementation 'com.getpebble:pebblekit:4.0.1@aar' - //implementation 'com.jaredrummler:android-device-names:1.1.7' + implementation 'com.jaredrummler:android-device-names:1.1.7' - implementation 'com.android.support:percent:27.1.1' - implementation 'com.android.support:support-v13:27.1.1' - implementation 'com.android.support:design:27.1.1' - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support:recyclerview-v7:27.1.1' - implementation 'com.android.support:cardview-v7:27.1.1' + implementation 'com.github.thorbenprimke:realm-recyclerview:0.9.25' + + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:support-v13:28.0.0' + implementation 'com.android.support:design:28.0.0' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:recyclerview-v7:28.0.0' + implementation 'com.android.support:cardview-v7:28.0.0' + implementation 'com.android.support:support-v4:28.0.0' implementation 'org.apache.commons:commons-lang3:3.4' @@ -141,7 +147,7 @@ dependencies { // due to bug in GraphView v4.2.1 using setNumHorizontalLabels reverted to using v4.0.1 implementation 'com.jjoe64:graphview:4.0.1' - implementation 'com.google.code.gson:gson:2.8.0' + implementation 'com.google.code.gson:gson:2.8.2' implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0' @@ -158,4 +164,4 @@ dependencies { implementation 'com.mikepenz:ionicons-typeface:2.0.1.3@aar' } -apply from: "signing.gradle" \ No newline at end of file +apply from: "signing.gradle" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 07132f19b7e21e2b0327654100eb3f259147bce6..5ee030665b224b48f760191f562b29940ae86770 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,16 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" package="info.nightscout.android"> -<!-- - Pogman: not sure what these are for? are they needed? - <uses-sdk android:minSdkVersion="14" android:maxSdkVersion="27" - tools:overrideLibrary="com.github.mikephil.charting.data.realm"/> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ---> + <uses-feature android:name="android.hardware.usb.host" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> diff --git a/app/src/main/java/info/nightscout/android/AutoStartActivity.java b/app/src/main/java/info/nightscout/android/AutoStartActivity.java index 34a2662c7180a2e7fd40716d7285e4a947747ac5..cf17c61deaecb89102b4a821acd000f0ff530d46 100644 --- a/app/src/main/java/info/nightscout/android/AutoStartActivity.java +++ b/app/src/main/java/info/nightscout/android/AutoStartActivity.java @@ -41,22 +41,7 @@ public class AutoStartActivity extends AppCompatActivity { } if (service) { - - try { - if (Realm.compactRealm(Realm.getDefaultConfiguration())) - Log.i(TAG, "compactRealm: default successful"); - if (Realm.compactRealm(UploaderApplication.getStoreConfiguration())) - Log.i(TAG, "compactRealm: store successful"); - if (Realm.compactRealm(UploaderApplication.getUserLogConfiguration())) - Log.i(TAG, "compactRealm: userlog successful"); - if (Realm.compactRealm(UploaderApplication.getHistoryConfiguration())) - Log.i(TAG, "compactRealm: history successful"); - } catch (Exception e) { - Log.e(TAG, "Error trying to compact realm" + Log.getStackTraceString(e)); - } - Log.d(TAG, "MasterService auto starter, starting!"); - startService(new Intent(getBaseContext(), MasterService.class)); } diff --git a/app/src/main/java/info/nightscout/android/history/NightscoutItem.java b/app/src/main/java/info/nightscout/android/history/NightscoutItem.java index aab1002a88a9658388024ddb444159b9e02016c1..972da92f4a303f324bb44e8bf5bbc16b979081bc 100644 --- a/app/src/main/java/info/nightscout/android/history/NightscoutItem.java +++ b/app/src/main/java/info/nightscout/android/history/NightscoutItem.java @@ -1,8 +1,8 @@ package info.nightscout.android.history; -import info.nightscout.api.EntriesEndpoints; -import info.nightscout.api.ProfileEndpoints; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.EntriesEndpoints; +import info.nightscout.android.upload.nightscout.ProfileEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; /** * Created by Pogman on 22.11.17. diff --git a/app/src/main/java/info/nightscout/android/history/PumpHistoryHandler.java b/app/src/main/java/info/nightscout/android/history/PumpHistoryHandler.java index 4b08118c8e569069b50548f3773438e19559fbeb..62ea31495ec0ca9f20a2c305e034e673c9b1c2e9 100644 --- a/app/src/main/java/info/nightscout/android/history/PumpHistoryHandler.java +++ b/app/src/main/java/info/nightscout/android/history/PumpHistoryHandler.java @@ -1,7 +1,6 @@ package info.nightscout.android.history; import android.content.Context; -import android.content.Intent; import android.support.annotation.NonNull; import android.util.Log; import android.util.Pair; @@ -22,11 +21,11 @@ import info.nightscout.android.R; import info.nightscout.android.UploaderApplication; import info.nightscout.android.medtronic.MedtronicCnlReader; import info.nightscout.android.medtronic.Stats; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.medtronic.exception.ChecksumException; import info.nightscout.android.medtronic.exception.EncryptionException; import info.nightscout.android.medtronic.exception.UnexpectedMessageException; import info.nightscout.android.medtronic.message.ReadHistoryResponseMessage; -import info.nightscout.android.medtronic.service.MasterService; import info.nightscout.android.model.medtronicNg.HistorySegment; import info.nightscout.android.model.medtronicNg.PumpHistoryAlarm; import info.nightscout.android.model.medtronicNg.PumpHistoryBG; @@ -50,8 +49,6 @@ import io.realm.RealmList; import io.realm.RealmResults; import io.realm.Sort; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_REFRESH; - /** * Created by Pogman on 5.11.17. */ @@ -138,8 +135,9 @@ public class PumpHistoryHandler { long now = System.currentTimeMillis(); Date limitDate = new Date(now - sender.process); - String log = String.format("sender[%s] limitdate: %s",senderID, dateFormatter.format(limitDate)); - String logdb = ""; + StringBuilder log = new StringBuilder(); + StringBuilder logdb = new StringBuilder(); + log.append(String.format("sender[%s] limitdate: %s",senderID, dateFormatter.format(limitDate))); List<PumpHistoryInterface> records = new ArrayList<>(); @@ -154,11 +152,11 @@ public class PumpHistoryHandler { records.addAll(requested); - if (requested.size() > 0) logdb += " " + dBitem.historydb + ": " + requested.size(); + if (requested.size() > 0) logdb.append(String.format(" %s: %s", dBitem.historydb, requested.size())); } } - log += " requested: " + records.size(); + log.append(String.format(" requested: %s", records.size())); // sort complete list from oldest to newest Collections.sort(records, new Comparator<PumpHistoryInterface>() { @@ -173,8 +171,9 @@ public class PumpHistoryHandler { if (records.size() > sender.limiter) records = records.subList(records.size() - sender.limiter, records.size()); - log += " limiter: " + sender.limiter + " final: " + records.size(); - Log.d(TAG, log + logdb); + log.append(String.format(" limiter: %s final: %s", sender.limiter, records.size())); + log.append(logdb.toString()); + Log.d(TAG, log.toString()); return records; } @@ -277,16 +276,6 @@ public class PumpHistoryHandler { } - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - mContext.sendBroadcast(intent); - } catch (Exception ignored) { - } - } - public int records() { int total = 0; for (DBitem dBitem : historyDB) { @@ -441,13 +430,13 @@ public class PumpHistoryHandler { public void profile(final MedtronicCnlReader cnlReader) throws EncryptionException, IOException, ChecksumException, TimeoutException, UnexpectedMessageException { - userLogMessage(mContext.getString(R.string.history_reading_pump_basal)); + UserLogMessage.send(mContext, R.string.history_reading_pump_basal); final byte[] basalPatterns = cnlReader.getBasalPatterns(); - userLogMessage(mContext.getString(R.string.history_reading_carb)); + UserLogMessage.send(mContext, R.string.history_reading_carb); final byte[] carbRatios = cnlReader.getBolusWizardCarbRatios(); - userLogMessage(mContext.getString(R.string.history_reading_sensitivity)); + UserLogMessage.send(mContext, R.string.history_reading_sensitivity); final byte[] sensitivity = cnlReader.getBolusWizardSensitivity(); - userLogMessage(mContext.getString(R.string.history_reading_targets)); + UserLogMessage.send(mContext, R.string.history_reading_targets); final byte[] targets = cnlReader.getBolusWizardTargets(); // user settings @@ -495,7 +484,7 @@ public class PumpHistoryHandler { } }); - userLogMessage(mContext.getString(R.string.history_sending_profile)); + UserLogMessage.send(mContext, R.string.history_sending_profile); // update NS historical treatments when pattern naming has changed @@ -508,7 +497,7 @@ public class PumpHistoryHandler { if (results.size() > 0) { Log.d(TAG, "NameBasalPatternChanged: Found " + results.size() + " pattern switch treatments to update"); - userLogMessage(mContext.getString(R.string.history_pattern_names_changed)); + UserLogMessage.send(mContext, R.string.history_pattern_names_changed); historyRealm.executeTransaction(new Realm.Transaction() { @Override @@ -544,7 +533,7 @@ public class PumpHistoryHandler { if (results.size() > 0) { Log.d(TAG, "GramsPerExchangeChanged: Found " + results.size() + " carb/bolus treatments to update"); - userLogMessage(mContext.getString(R.string.history_grams_changed)); + UserLogMessage.send(mContext, R.string.history_grams_changed); historyRealm.executeTransaction(new Realm.Transaction() { @Override @@ -604,8 +593,8 @@ public class PumpHistoryHandler { // estimate sgv? if (dataStore.isSysEnableEstimateSGV() && (PumpHistoryParser.CGM_EXCEPTION.SENSOR_CAL_NEEDED.equals(exception) - || PumpHistoryParser.CGM_EXCEPTION.SENSOR_END_OF_LIFE.equals(exception) - || (PumpHistoryParser.CGM_EXCEPTION.SENSOR_CAL_PENDING.equals(exception) && sgv == 0)) + || PumpHistoryParser.CGM_EXCEPTION.SENSOR_END_OF_LIFE.equals(exception) + || (PumpHistoryParser.CGM_EXCEPTION.SENSOR_CAL_PENDING.equals(exception) && sgv == 0)) ) { RealmResults<PumpHistoryMisc> miscResults = historyRealm @@ -632,7 +621,8 @@ public class PumpHistoryHandler { backfill = true; estimate = true; ((StatPoll) Stats.getInstance().readRecord(StatPoll.class)).incHistoryReqEstimate(); - userLogMessage(ICON_REFRESH + mContext.getString(R.string.history_text) + ": " + "estimate sgv"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: estimate sgv", R.string.history_text)); } } } @@ -645,7 +635,8 @@ public class PumpHistoryHandler { !PumpHistoryParser.CGM_EXCEPTION.SENSOR_INIT.equals(cgmResults.first().getSensorException())) { backfill = true; ((StatPoll) Stats.getInstance().readRecord(StatPoll.class)).incHistoryReqBackfill(); - userLogMessage(ICON_REFRESH + mContext.getString(R.string.history_text) + ": " + mContext.getString(R.string.history_cgm_backfill)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_cgm_backfill)); } } @@ -820,31 +811,41 @@ public class PumpHistoryHandler { if (sgv > 0 && cgmResults.get(i).getSgv() == 0 && !cgmResults.get(i).isEstimate()) { // don't flood the userlog during extensive backfills - if (cgmResults.size() - i <= 1) { - userLogMessage(String.format("estimated SGV: ¦%s¦ at: %s", - (int) Math.round(sgv), - dfUserlog.format(cgmResults.get(i).getEventDate()) - )); - - if (dataStore.isDbgEnableExtendedErrors()) { - userLogMessage(String.format("isig:%s vctr:%s roc:%s s:%s/%s %s%s%s%s%s", - cgmResults.get(i).getIsig(), - cgmResults.get(i).getVctr(), - cgmResults.get(i).getRateOfChange(), - cgmResults.get(i).getSensorStatus(), - cgmResults.get(i).getReadingStatus(), - cgmResults.get(i).isNoisyData() ? "N" : "", - cgmResults.get(i).isDiscardData() ? "D" : "", - cgmResults.get(i).isSensorError() ? "E" : "", - cgmResults.get(i).isBackfilledData() ? "B" : "", - cgmResults.get(i).isSettingsChanged() ? "S" : "" - )); - userLogMessage(String.format("t+%s f:%s o:%s x:%s", - t, - factor, - dfNumber.format(offsetAvg), - dfNumber.format(x) - )); + if (cgmResults.size() - i <= 5) { + + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.ESTIMATE, + String.format("estimated SGV {sgv;%s} at {time.sgv;%s}", + (int) Math.round(sgv), + cgmResults.get(i).getEventDate().getTime() + )); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.ESTIMATE, + String.format("estimated SGV {sgv;%s} at {time.sgv.e;%s}", + (int) Math.round(sgv), + cgmResults.get(i).getEventDate().getTime() + )); + + if (cgmResults.size() - i <= 1) { + + UserLogMessage.sendE(mContext, + String.format("isig: %s vctr: %s roc: %s s: %s/%s %s%s%s%s%s", + cgmResults.get(i).getIsig(), + cgmResults.get(i).getVctr(), + cgmResults.get(i).getRateOfChange(), + cgmResults.get(i).getSensorStatus(), + cgmResults.get(i).getReadingStatus(), + cgmResults.get(i).isNoisyData() ? "N" : "", + cgmResults.get(i).isDiscardData() ? "D" : "", + cgmResults.get(i).isSensorError() ? "E" : "", + cgmResults.get(i).isBackfilledData() ? "B" : "", + cgmResults.get(i).isSettingsChanged() ? "S" : "" + )); + UserLogMessage.sendE(mContext, + String.format("t+%s f: %s o: %s x: %s", + t, + factor, + dfNumber.format(offsetAvg), + dfNumber.format(x) + )); } } @@ -873,7 +874,8 @@ public class PumpHistoryHandler { .sort("eventDate", Sort.ASCENDING) .findAll(); if (cgmResults.size() > 0) { - userLogMessage(String.format("isig:%s vctr:%s roc:%s s:%s/%s %s%s%s%s%s", + UserLogMessage.send(mContext, + String.format("isig: %s vctr: %s roc: %s s: %s/%s %s%s%s%s%s", cgmResults.last().getIsig(), cgmResults.last().getVctr(), cgmResults.last().getRateOfChange(), @@ -1075,12 +1077,11 @@ public class PumpHistoryHandler { dateFormatter.format(end) )); - userLogMessage(String.format(Locale.getDefault(), "%s: %s \n %s - %s", - userlogTAG, - mContext.getString(R.string.history_requested), - dateFormatter.format(start), - dateFormatter.format(end)) - ); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.REQUESTED, + String.format("%s {id;%s}", userlogTAG, R.string.history_requested)); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.REQUESTED, + String.format("%s {id;%s}\n {time.hist.e;%s} - {time.hist.e;%s}", + userlogTAG, R.string.history_requested, start, end)); Date[] range; ReadHistoryResponseMessage response = cnlReader.getHistory(start, end, historyType); @@ -1095,7 +1096,7 @@ public class PumpHistoryHandler { long parseFrom = 0; if (segment.get(0).getFromDate().getTime() == segment.get(0).getToDate().getTime() && segment.get(1).getFromDate().getTime() != segment.get(1).getToDate().getTime()) - parseFrom = segment.get(1).getToDate().getTime(); + parseFrom = segment.get(1).getToDate().getTime() - 30 * 60000L; long timer = System.currentTimeMillis(); range = new PumpHistoryParser(response.getEventData()).process( @@ -1111,18 +1112,15 @@ public class PumpHistoryHandler { Log.d(TAG, logTAG + " parser processing took " + timer + "ms"); } - Log.d(TAG, String.format("%s received: %s - %s", - logTAG, + Log.d(TAG, String.format("%s received: %s - %s", logTAG, range[0] == null ? "null" : dateFormatter.format(range[0]), - range[1] == null ? "null" : dateFormatter.format(range[1]) - )); + range[1] == null ? "null" : dateFormatter.format(range[1]))); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(String.format(Locale.getDefault(), "%s: %s \n %s - %s", - userlogTAG, - mContext.getString(R.string.history_received), - range[0] == null ? "null" : dateFormatter.format(range[0]), - range[1] == null ? "null" : dateFormatter.format(range[1]))); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.RECEIVED, + String.format("%s {id;%s}\n {time.hist.e;%s} - {time.hist.e;%s}", + userlogTAG, R.string.history_received, + range[0] == null ? 0 : range[0].getTime(), + range[1] == null ? 0 : range[1].getTime())); // final Date haveFrom = range[0] != null && range[0].getTime() < start ? range[0] : new Date(start); // final Date haveTo = range[1] != null && range[1].getTime() - start > -1000L ? range[1] : new Date(end); @@ -1135,21 +1133,6 @@ public class PumpHistoryHandler { dateFormatter.format(haveTo) )); -/* - -=== CGM history: adding history pull marker -=== CGM history: segment: 1/4 start: 30/09/2018 08:06:04 end: 30/09/2018 08:06:04 -=== CGM history: segment: 2/4 start: 30/09/2018 08:01:09 end: 30/09/2018 08:01:09 -=== CGM history: segment: 3/4 start: 11/07/2018 17:36:57 end: 30/09/2018 07:57:49 -=== CGM history: segment: 4/4 start: 02/06/2018 08:06:04 end: 02/06/2018 08:06:04 -=== CGM history: requested: 30/09/2018 08:01:09 - 30/09/2018 08:06:04 -=== CGM history: parser processing took 25ms -=== CGM history: received: 29/09/2018 20:12:50 - 30/09/2018 07:57:49 -=== CGM history: segment: 1/2 start: 11/07/2018 17:36:57 end: 30/09/2018 08:06:04 -=== CGM history: segment: 2/2 start: 02/06/2018 08:06:04 end: 02/06/2018 08:06:04 - -*/ - historyRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(@NonNull Realm realm) { diff --git a/app/src/main/java/info/nightscout/android/history/PumpHistorySender.java b/app/src/main/java/info/nightscout/android/history/PumpHistorySender.java index a294d2c33157904ff780fac4115e9e67828afe39..38c1d60f57300c62f0d02b9b1c0ca8c2e14de08c 100644 --- a/app/src/main/java/info/nightscout/android/history/PumpHistorySender.java +++ b/app/src/main/java/info/nightscout/android/history/PumpHistorySender.java @@ -330,8 +330,8 @@ public class PumpHistorySender { String db = record.getClass().getSuperclass().getSimpleName(); for (Sender sender : senders) { - if (sender.active.contains(db)) - if (!req.contains(sender.id)) req += sender.id; + if (sender.active.contains(db) && !req.contains(sender.id)) + req = req.concat(sender.id); } record.setSenderREQ(req); @@ -343,8 +343,8 @@ public class PumpHistorySender { String db = record.getClass().getSuperclass().getSimpleName(); for (Sender sender : senders) { - if (sender.active.contains(db)) - if (!ack.contains(sender.id)) ack += sender.id; + if (sender.active.contains(db) && !ack.contains(sender.id)) + ack = ack.concat(sender.id); } record.setSenderACK(ack); diff --git a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java index cd50511267ad585df7556de5c7c6cccedf099fae..252be87537314a763da28b4ce20c5c1b4a46ae53 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -16,8 +17,11 @@ import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.Settings; import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.menu.MenuView; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.format.DateUtils; import android.util.DisplayMetrics; @@ -26,15 +30,14 @@ import android.util.TypedValue; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; -import android.widget.ScrollView; import android.widget.TextView; -import android.widget.TextView.BufferType; import android.widget.Toast; import com.github.javiersantos.appupdater.AppUpdater; import com.github.javiersantos.appupdater.enums.UpdateFrom; -//import com.jaredrummler.android.device.DeviceName; +import com.jaredrummler.android.device.DeviceName; import com.jjoe64.graphview.DefaultLabelFormatter; import com.jjoe64.graphview.GraphView; import com.jjoe64.graphview.series.DataPointInterface; @@ -49,13 +52,10 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import java.io.Serializable; -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; +import co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView; import info.nightscout.android.R; import info.nightscout.android.UploaderApplication; import info.nightscout.android.eula.Eula; @@ -66,6 +66,8 @@ import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.settings.SettingsActivity; import info.nightscout.android.model.store.DataStore; import info.nightscout.android.model.store.UserLog; +import info.nightscout.android.utils.FormatKit; +import info.nightscout.android.utils.RealmKit; import io.realm.OrderedCollectionChangeSet; import io.realm.OrderedRealmCollectionChangeListener; import io.realm.Realm; @@ -73,84 +75,57 @@ import io.realm.RealmResults; import io.realm.Sort; import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_HEART; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_INFO; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_SETTING; -import static org.apache.commons.lang3.math.NumberUtils.toInt; - public class MainActivity extends AppCompatActivity implements OnSharedPreferenceChangeListener, OnEulaAgreedTo { private static final String TAG = MainActivity.class.getSimpleName(); public static final float MMOLXLFACTOR = 18.016f; - private UserLogMessage userLogMessage = UserLogMessage.getInstance(); - - private int chartZoom = 3; - private boolean hasZoomedChart = false; - - private boolean mEnableCgmService;// = true; - private SharedPreferences prefs;// = null; + private Context mContext; - private TextView mTextViewLog; // This will eventually move to a status page. - private TextView mTextViewLogButtonTop; - private TextView mTextViewLogButtonTopRecent; - private TextView mTextViewLogButtonBottom; - private TextView mTextViewLogButtonBottomRecent; + private boolean mEnableCgmService; + private SharedPreferences mPrefs; - private ScrollView mScrollView; private GraphView mChart; + private int chartZoom; + private Handler mUiRefreshHandler = new Handler(); private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable(); private Realm mRealm; private Realm storeRealm; - private Realm userLogRealm; private Realm historyRealm; private DataStore dataStore; + private UserLogDisplay userLogDisplay; + @Override public void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate called"); super.onCreate(savedInstanceState); - try { - if (Realm.compactRealm(Realm.getDefaultConfiguration())) - Log.i(TAG, "compactRealm: default successful"); - if (Realm.compactRealm(UploaderApplication.getStoreConfiguration())) - Log.i(TAG, "compactRealm: store successful"); - if (Realm.compactRealm(UploaderApplication.getUserLogConfiguration())) - Log.i(TAG, "compactRealm: userlog successful"); - if (Realm.compactRealm(UploaderApplication.getHistoryConfiguration())) - Log.i(TAG, "compactRealm: history successful"); - } catch (Exception e) { - Log.e(TAG, "Error trying to compact realm" + Log.getStackTraceString(e)); - } + mContext = this.getBaseContext(); + + RealmKit.compact(mContext); storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration()); - dataStore = storeRealm.where(DataStore.class).findFirst(); - if (dataStore == null) { - storeRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + dataStore = storeRealm.where(DataStore.class).findFirst(); + + if (dataStore == null) dataStore = realm.createObject(DataStore.class); - } - }); - } - // limit date for NS backfill sync period, set to this init date to stop overwrite of older NS data (pref option to override) - if (dataStore.getNightscoutLimitDate() == null) { - storeRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { + // limit date for NS backfill sync period, set to this init date to stop overwrite of older NS data (pref option to override) + if (dataStore.getNightscoutLimitDate() == null) dataStore.setNightscoutLimitDate(new Date(System.currentTimeMillis())); - } - }); - } + } + }); - prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); - prefs.registerOnSharedPreferenceChangeListener(this); - copyPrefsToDataStore(prefs); + mPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + mPrefs.registerOnSharedPreferenceChangeListener(this); + copyPrefsToDataStore(mPrefs); setContentView(R.layout.activity_main); @@ -175,8 +150,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } } - mEnableCgmService = Eula.show(this, prefs) - && prefs.getBoolean(getString(R.string.preference_eula_accepted), false); + mEnableCgmService = Eula.show(this, mPrefs) + && mPrefs.getBoolean(getString(R.string.preference_eula_accepted), false); Toolbar toolbar = findViewById(R.id.toolbar); if (toolbar != null) { @@ -250,24 +225,22 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } else if (drawerItem.equals(itemGetNow)) { // It was triggered by user so start reading of data now and not based on last poll. if (mEnableCgmService) { - userLogMessage.add(getString(R.string.main_requesting_poll_now)); sendBroadcast(new Intent(MasterService.Constants.ACTION_READ_NOW)); } else { - userLogMessage.add(getString(R.string.main_cgm_service_disabled)); + UserLogMessage.getInstance().add(R.string.main_cgm_service_disabled); } } else if (drawerItem.equals(itemUpdateProfile)) { if (mEnableCgmService) { if (dataStore.isNsEnableProfileUpload()) { - userLogMessage.add(getString(R.string.main_requesting_pump_profile)); sendBroadcast(new Intent(MasterService.Constants.ACTION_READ_PROFILE)); } else { - userLogMessage.add(getString(R.string.main_pump_profile_disabled)); + UserLogMessage.getInstance().add(getString(R.string.main_pump_profile_disabled)); } } else { - userLogMessage.add(getString(R.string.main_cgm_service_disabled)); + UserLogMessage.getInstance().add(R.string.main_cgm_service_disabled); } } else if (drawerItem.equals(itemClearLog)) { - userLogMessage.clear(); + UserLogMessage.getInstance().clear(); } else if (drawerItem.equals(itemCheckForUpdate)) { checkForUpdateNow(); } @@ -277,44 +250,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc }) .build(); - mTextViewLog = findViewById(R.id.textview_log); - mScrollView = findViewById(R.id.scrollView); - mScrollView.setSmoothScrollingEnabled(true); - mTextViewLogButtonTop = findViewById(R.id.button_log_top); - mTextViewLogButtonTop.setVisibility(View.GONE); - mTextViewLogButtonTop.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - changeUserLogViewOlder(); - } - }); - mTextViewLogButtonTopRecent = findViewById(R.id.button_log_top_recent); - mTextViewLogButtonTopRecent.setVisibility(View.GONE); - mTextViewLogButtonTopRecent.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - changeUserLogViewRecent(); - } - }); - - mTextViewLogButtonBottom = findViewById(R.id.button_log_bottom); - mTextViewLogButtonBottom.setVisibility(View.GONE); - mTextViewLogButtonBottom.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - changeUserLogViewNewer(); - } - }); - mTextViewLogButtonBottomRecent = findViewById(R.id.button_log_bottom_recent); - mTextViewLogButtonBottomRecent.setVisibility(View.GONE); - mTextViewLogButtonBottomRecent.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - changeUserLogViewRecent(); - } - }); - - chartZoom = Integer.parseInt(prefs.getString("chartZoom", "3")); + chartZoom = Integer.parseInt(mPrefs.getString("chartZoom", "3")); mChart = findViewById(R.id.chart); @@ -361,11 +297,12 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc default: chartZoom = 1; } - prefs.edit().putString("chartZoom", Integer.toString(chartZoom)).commit(); + mPrefs.edit().putString("chartZoom", Integer.toString(chartZoom)).apply(); refreshDisplayChart(); String text = chartZoom + " hour chart"; - Toast.makeText(getBaseContext(), text, Toast.LENGTH_SHORT).show(); + Snackbar.make(v, text, Snackbar.LENGTH_SHORT) + .setAction("Action", null).show(); return true; } @@ -381,62 +318,44 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc // due to bug in GraphView v4.2.1 using setNumHorizontalLabels reverted to using v4.0.1 and setHumanRounding is n/a in this version // mChart.getGridLabelRenderer().setHumanRounding(false); + final int orientation = getResources().getConfiguration().orientation; mChart.getGridLabelRenderer().setLabelFormatter( new DefaultLabelFormatter() { - //DateFormat mFormat = new SimpleDateFormat("HH:mm", Locale.US); // 24 hour format forced to fix label overlap - DateFormat mFormat = new SimpleDateFormat("h:mm", Locale.getDefault()); - @Override public String formatLabel(double value, boolean isValueX) { - if (isValueX) { - //return FormatKit.getInstance().formatAsClock((long) value); - return mFormat.format(new Date((long) value)); - } else { - return strFormatSGV(value); - } + if (!isValueX) + return FormatKit.getInstance().formatAsGlucose((int) value); + else if (orientation == Configuration.ORIENTATION_LANDSCAPE) + return FormatKit.getInstance().formatAsClock((long) value); + else + return FormatKit.getInstance().formatAsClockNoAmPm((long) value); } } ); - /* - DeviceName.with(this).request(new DeviceName.Callback() { - - @Override public void onFinished(DeviceName.DeviceInfo info, Exception error) { - String manufacturer = info.manufacturer; // "Samsung" - String name = info.marketName; // "Galaxy S8+" - String model = info.model; // "SM-G955W" - String codename = info.codename; // "dream2qltecan" - String deviceName = info.getName(); // "Galaxy S8+" - - Log.i(TAG, "manufacturer = " + info.manufacturer); - Log.i(TAG, "name = " + info.marketName); - Log.i(TAG, "model = " + info.model); - Log.i(TAG, "codename = " + info.codename); - Log.i(TAG, "deviceName = " + info.getName()); - } - }); - */ - + userLogDisplay = new UserLogDisplay(mContext); } @Override protected void onStart() { Log.d(TAG, "onStart called"); super.onStart(); - if (userLogRealm == null) userLogRealm = Realm.getInstance(UploaderApplication.getUserLogConfiguration()); - if (historyRealm == null) historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration()); + if (historyRealm == null) + historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration()); if (mRealm == null) mRealm = Realm.getDefaultInstance(); checkForUpdateBackground(5); startDisplay(); - startUserLogView(); + userLogDisplay.start(dataStore.isDbgEnableExtendedErrors()); } @Override protected void onResume() { Log.d(TAG, "onResume called"); super.onResume(); + + if (userLogDisplay != null) userLogDisplay.focusCurrent(); } protected void onPause() { @@ -449,13 +368,11 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc Log.d(TAG, "onStop called"); super.onStop(); - stopUserLogView(); + if (userLogDisplay != null) userLogDisplay.stop(); stopDisplay(); - if (userLogRealm != null && !userLogRealm.isClosed()) userLogRealm.close(); if (historyRealm != null && !historyRealm.isClosed()) historyRealm.close(); if (mRealm != null && !mRealm.isClosed()) mRealm.close(); - userLogRealm = null; historyRealm = null; mRealm = null; } @@ -467,11 +384,10 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (!mEnableCgmService) stopMasterService(); - statusDestroy(); + shutdownMessage(); PreferenceManager.getDefaultSharedPreferences(getBaseContext()).unregisterOnSharedPreferenceChangeListener(this); - if (storeRealm !=null && !storeRealm.isClosed()) storeRealm.close(); - if (userLogRealm != null && !userLogRealm.isClosed()) userLogRealm.close(); + if (storeRealm != null && !storeRealm.isClosed()) storeRealm.close(); if (historyRealm != null && !historyRealm.isClosed()) historyRealm.close(); if (mRealm != null && !mRealm.isClosed()) mRealm.close(); } @@ -480,7 +396,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc protected void onPostCreate(Bundle savedInstanceState) { Log.d(TAG, "onPostCreate called"); super.onPostCreate(savedInstanceState); - statusStartup(); + startupMessage(); startMasterService(); } @@ -527,57 +443,112 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc .start(); } - private void statusStartup() { - if (!prefs.getBoolean("EnableCgmService", false)) { - userLogMessage.add(ICON_HEART + getString(R.string.main_hello)); - userLogMessage.add(ICON_SETTING + String.format(Locale.getDefault(), "%s %s", - getString(R.string.main_uploading), - dataStore.isNightscoutUpload() ? getString(R.string.text_enabled) : getString(R.string.text_disabled))); - userLogMessage.add(ICON_SETTING + String.format(Locale.getDefault(), "%s %s", - getString(R.string.main_treatments), - dataStore.isNightscoutUpload() ? getString(R.string.text_enabled) : getString(R.string.text_disabled))); - userLogMessage.add(ICON_SETTING + String.format(Locale.getDefault(), "%s %d %s", - getString(R.string.main_poll_interval), - dataStore.getLowBatPollInterval() / 60000, - getString(R.string.time_min))); - userLogMessage.add(ICON_SETTING + String.format(Locale.getDefault(), "%s %d %s", - getString(R.string.main_low_battery_poll_interval), - dataStore.getLowBatPollInterval() / 60000, - getString(R.string.time_min))); + private void startupMessage() { + // userlog message at startup when no service is running + if (!mPrefs.getBoolean("EnableCgmService", false)) { + + UserLogMessage.getInstance().add(UserLogMessage.TYPE.HEART, R.string.main_hello); + + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} {id;%s}", + R.string.main_uploading, + dataStore.isNightscoutUpload() ? R.string.text_enabled : R.string.text_disabled)); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} {id;%s}", + R.string.main_treatments, + dataStore.isNsEnableTreatments() ? R.string.text_enabled : R.string.text_disabled + )); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} %s {id;%s}", + R.string.main_poll_interval, + dataStore.getPollInterval() / 60000L, + R.string.time_min + )); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} %s {id;%s}", + R.string.main_low_battery_poll_interval, + dataStore.getLowBatPollInterval() / 60000L, + R.string.time_min + )); + int historyFrequency = dataStore.getSysPumpHistoryFrequency(); if (historyFrequency > 0) { - userLogMessage.add(ICON_SETTING + String.format(Locale.getDefault(), "%s %d %s", - getString(R.string.main_auto_mode_update), - historyFrequency, - getString(R.string.time_min))); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} %s {id;%s}", + R.string.main_auto_mode_update, + historyFrequency, + R.string.time_min + )); } else { - userLogMessage.add(ICON_SETTING + getString(R.string.main_auto_mode_update) + " " + getString(R.string.main_events_only)); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.OPTION, + String.format("{id;%s} {id;%s}", + R.string.main_auto_mode_update, + R.string.main_events_only + )); } + + deviceMessage(); } } - private void statusDestroy() { - if (!prefs.getBoolean("EnableCgmService", false)) { - userLogMessage.add(ICON_HEART + getString(R.string.main_goodbye)); - userLogMessage.add("---------------------------------------------------"); + private void shutdownMessage() { + // userlog message at shutdown when 'stop collecting data' selected + if (!mPrefs.getBoolean("EnableCgmService", false)) { + UserLogMessage.getInstance().add(UserLogMessage.TYPE.HEART, R.string.main_goodbye); + UserLogMessage.getInstance().add("---------------------------------------------------"); } } + private void deviceMessage() { + DeviceName.with(this).request(new DeviceName.Callback() { + + @Override + public void onFinished(DeviceName.DeviceInfo info, Exception error) { + String manufacturer = info.manufacturer; // "Samsung" + String marketName = info.marketName; // "Galaxy S8+" + String model = info.model; // "SM-G955W" + String codename = info.codename; // "dream2qltecan" + String deviceName = info.getName(); // "Galaxy S8+" + String androidSDK = String.valueOf(Build.VERSION.SDK_INT); + String androidVERSION = Build.VERSION.RELEASE; + + Log.i(TAG, "manufacturer = " + manufacturer); + Log.i(TAG, "name = " + marketName); + Log.i(TAG, "model = " + model); + Log.i(TAG, "codename = " + codename); + Log.i(TAG, "deviceName = " + deviceName); + Log.i(TAG, "androidSDK = " + androidSDK); + Log.i(TAG, "androidVERSION = " + androidVERSION); + + UserLogMessage.getInstance().add(UserLogMessage.TYPE.NOTE, UserLogMessage.FLAG.EXTENDED, + String.format("Uploader device details:\n mfr: %s\n name: %s\n model: %s\n code: %s\n device: %s\n android sdk: %s ver: %s", + manufacturer, + marketName, + model, + codename, + deviceName, + androidSDK, + androidVERSION + )); + } + }); + } + private void startMasterService() { Log.i(TAG, "startMasterService called"); if (mEnableCgmService) { - prefs.edit().putBoolean("EnableCgmService", true).commit(); + mPrefs.edit().putBoolean("EnableCgmService", true).commit(); startService(new Intent(this, MasterService.class)); } else { - prefs.edit().putBoolean("EnableCgmService", false).commit(); + mPrefs.edit().putBoolean("EnableCgmService", false).commit(); Log.i(TAG, "startMasterService: CgmService is disabled"); } } private void stopMasterService() { Log.i(TAG, "stopMasterService called"); - userLogMessage.add(ICON_INFO + getString(R.string.main_shutting_down_cgm_service)); - prefs.edit().putBoolean("EnableCgmService", false).commit(); + UserLogMessage.getInstance().add(UserLogMessage.TYPE.INFO, R.string.main_shutting_down_cgm_service); + mPrefs.edit().putBoolean("EnableCgmService", false).commit(); sendBroadcast(new Intent(MasterService.Constants.ACTION_STOP_SERVICE)); } @@ -594,7 +565,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } } else if (key.equals("chartZoom")) { chartZoom = Integer.parseInt(sharedPreferences.getString("chartZoom", "3")); - hasZoomedChart = false; } else { copyPrefsToDataStore(sharedPreferences); if (mEnableCgmService) @@ -603,18 +573,12 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } public void copyPrefsToDataStore(final SharedPreferences sharedPreferences) { - - // prefs that are in constant use, safe across threads and processes - - final Context context = this.getBaseContext(); - storeRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(@NonNull Realm realm) { - dataStore.copyPrefs(context, sharedPreferences); + dataStore.copyPrefs(mContext, sharedPreferences); } }); - } @Override @@ -637,21 +601,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc startActivity(manageCNLIntent); } - private String strFormatSGV(double sgvValue) { - NumberFormat sgvFormatter; - if (dataStore.isMmolxl()) { - if (dataStore.isMmolxlDecimals()) { - sgvFormatter = new DecimalFormat("0.00"); - } else { - sgvFormatter = new DecimalFormat("0.0"); - } - return sgvFormatter.format(sgvValue / MMOLXLFACTOR); - } else { - sgvFormatter = new DecimalFormat("0"); - return sgvFormatter.format(sgvValue); - } - } - private RealmResults displayPumpResults; private RealmResults displayCgmResults; private long timeLastSGV; @@ -676,15 +625,13 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc displayPumpResults = mRealm.where(PumpStatusEvent.class) .sort("eventDate", Sort.ASCENDING) - .findAllAsync(); + .findAll(); displayPumpResults.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults>() { @Override public void onChange(@NonNull RealmResults realmResults, OrderedCollectionChangeSet changeSet) { - if (changeSet != null) { - if (changeSet.getInsertions().length > 0) { - refreshDisplayPump(); - } + if (changeSet != null && changeSet.getInsertions().length > 0) { + refreshDisplayPump(); } } }); @@ -707,23 +654,21 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc displayCgmResults = historyRealm.where(PumpHistoryCGM.class) .notEqualTo("sgv", 0) .sort("eventDate", Sort.ASCENDING) - .findAllAsync(); + .findAll(); displayCgmResults.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults>() { @Override public void onChange(@NonNull RealmResults realmResults, OrderedCollectionChangeSet changeSet) { - // initial refresh after start - if (changeSet == null) { - refreshDisplayCgm(); - refreshDisplayChart(); - } - // items added or changed - else if (changeSet.getInsertions().length + changeSet.getChanges().length > 0) { + if (changeSet != null && + changeSet.getInsertions().length + changeSet.getChanges().length > 0) { refreshDisplayCgm(); refreshDisplayChart(); } } }); + + refreshDisplayCgm(); + refreshDisplayChart(); } private void stopDisplayCgm() { @@ -784,7 +729,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (sgv_results.size() > 0) { timeLastSGV = sgv_results.last().getEventDate().getTime(); - sgvString = strFormatSGV(sgv_results.last().getSgv()); + sgvString = FormatKit.getInstance().formatAsGlucose(sgv_results.last().getSgv(), false, true); String trend = sgv_results.last().getCgmTrend(); if (trend != null) { @@ -925,18 +870,18 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc long maxX = timeLastSGV + 90000L; RealmResults<PumpHistoryCGM> minmaxY = results.where() - .greaterThan("eventDate", new Date(minX)) + .greaterThan("eventDate", new Date(minX)) .sort("sgv", Sort.ASCENDING) .findAll(); long rangeY, minRangeY; double minY = minmaxY.first().getSgv(); double maxY = minmaxY.last().getSgv(); - if (prefs.getBoolean("mmolxl", false)) { + if (mPrefs.getBoolean("mmolxl", false)) { minY = Math.floor((minY / MMOLXLFACTOR) * 2); maxY = Math.ceil((maxY / MMOLXLFACTOR) * 2); rangeY = (long) (maxY - minY); - minRangeY = ((rangeY / 4 ) + 1) * 4; + minRangeY = ((rangeY / 4) + 1) * 4; minY = minY - Math.floor((minRangeY - rangeY) / 2); maxY = minY + minRangeY; minY = Math.floor(minY * MMOLXLFACTOR / 2); @@ -945,7 +890,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc minY = Math.floor(minY / 10) * 10; maxY = Math.ceil(maxY / 10) * 10; rangeY = (long) (maxY - minY); - minRangeY = ((rangeY / 20 ) + 1) * 20; + minRangeY = ((rangeY / 20) + 1) * 20; minY = minY - Math.floor((minRangeY - rangeY) / 2); maxY = minY + minRangeY; } @@ -978,15 +923,13 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc PointsGraphSeries sgvSeries = new PointsGraphSeries(entries); sgvSeries.setOnDataPointTapListener(new OnDataPointTapListener() { - DateFormat mFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM); - @Override public void onTap(Series series, DataPointInterface dataPoint) { - double sgv = dataPoint.getY(); - - StringBuilder sb = new StringBuilder(mFormat.format(new Date((long) dataPoint.getX())) + ": "); - sb.append(strFormatSGV(sgv)); - Toast.makeText(getBaseContext(), sb.toString(), Toast.LENGTH_SHORT).show(); + String s = String.format("%s ~ %s", + FormatKit.getInstance().formatAsDayClock((long) dataPoint.getX()), + FormatKit.getInstance().formatAsGlucose((int) dataPoint.getY(), true, true) + ); + Toast.makeText(getBaseContext(), s, Toast.LENGTH_SHORT).show(); } }); @@ -996,7 +939,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc double sgv = dataPoint.getY(); if (((MainActivity.DataPoint) dataPoint).isEstimate()) - paint.setColor(Color.BLUE); + paint.setColor(0xFF0080FF); else if (sgv < 80) paint.setColor(Color.RED); @@ -1042,7 +985,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } private class DataPoint implements DataPointInterface, Serializable { - private static final long serialVersionUID=1428263322645L; + private static final long serialVersionUID = 1428263322645L; private double x; private double y; @@ -1071,7 +1014,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc @Override public String toString() { - return "["+x+"/"+y+"]"; + return "[" + x + "/" + y + "]"; } } @@ -1080,161 +1023,261 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics); } + private class UserLogDisplay { - private final int PAGE_SIZE = 300; - private final int FIRSTPAGE_SIZE = 75; //100; - private int viewPosition = 0; - private RealmResults userLogResults; + private Context context; - private void stopUserLogView() { - Log.d(TAG, "stopUserLogView"); - if (userLogResults != null) userLogResults.removeAllChangeListeners(); - userLogResults = null; - } + private FloatingActionButton fabCurrent; + private FloatingActionButton fabSearch; - private void startUserLogView() { - Log.d(TAG, "startUserLogView"); - viewPosition = 0; + private RealmRecyclerView realmRecyclerView; + private UserLogAdapter adapter; - userLogResults = userLogRealm.where(UserLog.class) - .sort("timestamp", Sort.DESCENDING) - .findAllAsync(); + private Realm userLogRealm; + private RealmResults<UserLog> userLogResults; - userLogResults.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults>() { - @Override - public void onChange(@NonNull RealmResults realmResults, OrderedCollectionChangeSet changeSet) { + private boolean autoScroll; + private boolean extended; - if (changeSet == null) { - changeUserLogViewRecent(); - return; - } + public UserLogDisplay(Context context) { + this.context = context; - if (userLogResults.size() > 0) { - if (viewPosition > 0) - viewPosition++; // move the view pointer when not on first page to keep aligned - } else { - Log.d(TAG, "UserLogView listener reset!!!"); - changeUserLogViewRecent(); - return; + realmRecyclerView = findViewById(R.id.recyclerview_log); + realmRecyclerView.getRecycleView().setHasFixedSize(true); + //realmRecyclerView.setItemViewCacheSize(30); + realmRecyclerView.setDrawingCacheEnabled(true); + realmRecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); + + fabCurrent = findViewById(R.id.fab_log_current); + fabCurrent.hide(); + + fabCurrent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + if (userLogResults != null && adapter != null && realmRecyclerView != null) { + + int t = adapter.getItemCount(); + int c = realmRecyclerView.getChildCount(); + if (c == 0) return; + int p = realmRecyclerView.findFirstVisibleItemPosition(); + if (p < 0 || p > userLogResults.size() - 1) return; + + if (t - c - p > 200) { + realmRecyclerView.scrollToPosition(t - 1); + } else { + realmRecyclerView.smoothScrollToPosition(t - 1); + } + + } } + }); - buildUserLogView(); + fabSearch = findViewById(R.id.fab_log_search); + fabSearch.hide(); - if (viewPosition == 0) { - // auto scroll status log - if ((mScrollView.getChildAt(0).getBottom() < mScrollView.getHeight()) || ((mScrollView.getChildAt(0).getBottom() - mScrollView.getScrollY() - mScrollView.getHeight()) < (mScrollView.getHeight() / 3))) { - mScrollView.post(new Runnable() { - public void run() { - mScrollView.fullScroll(View.FOCUS_DOWN); + fabSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + if (userLogResults != null && adapter != null && realmRecyclerView != null) { + int p = realmRecyclerView.findFirstVisibleItemPosition(); + if (p >= 0 && p < userLogResults.size()) { + + RealmResults<UserLog> rr = userLogResults.where() + .lessThan("timestamp", userLogResults.get(p).getTimestamp()) + .beginGroup() + .equalTo("type", UserLogMessage.TYPE.WARN.value()) + .or() + .equalTo("type", UserLogMessage.TYPE.NOTE.value()) + .endGroup() + .sort("timestamp", Sort.DESCENDING) + .findAll(); + + if (rr.size() > 0) { + int ss = userLogResults.indexOf(rr.first()); + int c = realmRecyclerView.getRecycleView().getLayoutManager().getChildCount() / 4; + int to = ss - (c < 1 ? 1 : c); + if (to < 0) to = 0; + if (Math.abs(p - to) > 400) + realmRecyclerView.scrollToPosition(to); + else + realmRecyclerView.smoothScrollToPosition(to); } - }); + } } } - } - }); - } + }); - private void buildUserLogView() { - int remain = userLogResults.size() - viewPosition; - int segment = remain; - if (viewPosition == 0 && segment > FIRSTPAGE_SIZE) segment = FIRSTPAGE_SIZE; - else if (segment > PAGE_SIZE) segment = PAGE_SIZE; - - RealmResults<UserLog> ul = userLogResults; - StringBuilder sb = new StringBuilder(); - if (segment > 0) { - for (int index = viewPosition; index < viewPosition + segment; index++) - sb.insert(0, formatMessage(ul.get(index)) + (sb.length() > 0 ? "\n" : "")); - } - mTextViewLog.setText(sb.toString(), BufferType.EDITABLE); + fabSearch.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + + if (userLogResults != null && adapter != null && realmRecyclerView != null) { + int p = realmRecyclerView.findFirstVisibleItemPosition(); + if (p >= 0 && p < userLogResults.size()) { + + RealmResults<UserLog> rr = userLogResults.where() + .lessThan("timestamp", userLogResults.get(p).getTimestamp()) + .equalTo("type", extended ? UserLogMessage.TYPE.NOTE.value() : UserLogMessage.TYPE.WARN.value()) + .sort("timestamp", Sort.DESCENDING) + .findAll(); + + if (rr.size() > 0) { + int ss = userLogResults.indexOf(rr.first()); + int c = realmRecyclerView.getRecycleView().getLayoutManager().getChildCount() / 4; + int to = ss - (c < 1 ? 1 : c); + if (to < 0) to = 0; + if (Math.abs(p - to) > 400) + realmRecyclerView.scrollToPosition(to); + else + realmRecyclerView.smoothScrollToPosition(to); + } + } + } + return true; + } + }); + + RecyclerView rv = realmRecyclerView.getRecycleView(); + rv.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + boolean fab = false; + if (userLogResults != null && adapter != null && realmRecyclerView != null) { + int t = adapter.getItemCount(); + int p = realmRecyclerView.findFirstVisibleItemPosition(); + if (p >= 0 && p < t && t - p > 20) + fab = true; + } + if (fab) { + fabCurrent.show(); + fabSearch.show(); + } else { + fabCurrent.hide(); + fabSearch.hide(); + } + } + }); + + rv.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { + @Override + public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { + if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) + autoScroll = false; + else + autoScroll = true; + return false; + } + + @Override + public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + } + }); - if (viewPosition > 0) { - mTextViewLogButtonBottom.setVisibility(View.VISIBLE); - mTextViewLogButtonBottomRecent.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonBottom.setVisibility(View.GONE); - mTextViewLogButtonBottomRecent.setVisibility(View.GONE); } - if (remain > segment) { - mTextViewLogButtonTop.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonTop.setVisibility(View.GONE); + + public void stop() { + Log.d(TAG, "stop"); + + fabCurrent.hide(); + fabSearch.hide(); + + if (adapter != null) { + if (realmRecyclerView.getRecycleView() != null && realmRecyclerView.getRecycleView().getLayoutManager() != null) + realmRecyclerView.getRecycleView().getLayoutManager().removeAllViews(); + realmRecyclerView.setAdapter(null); + adapter.close(); + adapter = null; + } + + if (userLogResults != null) { + userLogResults.removeAllChangeListeners(); + userLogResults = null; + } + + if (userLogRealm != null) { + if (!userLogRealm.isClosed()) userLogRealm.close(); + userLogRealm = null; + } } - if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) { - mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonTopRecent.setVisibility(View.GONE); + + public void focusCurrent() { + int lastPosition = userLogResults.size() - 1; + adapter.setLastAnimPosition(lastPosition); + realmRecyclerView.scrollToPosition(lastPosition); } - } - private String formatMessage(UserLog userLog) { -/* - SimpleDateFormat sdf = new SimpleDateFormat("E", Locale.getDefault()); - String clock = sdf.format(userLog.getTimestamp()); - if (android.text.format.DateFormat.is24HourFormat(this)) { - sdf.applyLocalizedPattern(" H:mm:ss"); - clock += sdf.format(userLog.getTimestamp()); - } else { - sdf.applyLocalizedPattern(" h:mm:ss a"); - clock += sdf.format(userLog.getTimestamp()).toLowerCase().replace(".", "").replace(",", ""); + public void start() { + start(false); } -*/ - SimpleDateFormat sdf = new SimpleDateFormat("E HH:mm:ss"); - String clock = sdf.format(userLog.getTimestamp()); - - String split[] = userLog.getMessage().split("¦"); - if (split.length == 2) - return clock + ": " + split[0] + strFormatSGV(toInt(split[1])); - else if (split.length == 3) - return clock + ": " + split[0] + strFormatSGV(toInt(split[1])) + split[2]; - else - return clock + ": " + split[0]; - } - private void changeUserLogViewOlder() { - if (viewPosition == 0) viewPosition += FIRSTPAGE_SIZE; - else viewPosition += PAGE_SIZE; - buildUserLogView(); - mScrollView.post(new Runnable() { - public void run() { - mScrollView.fullScroll(View.FOCUS_DOWN); - if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) { - mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonTopRecent.setVisibility(View.GONE); - } - } - }); - } + public void start(Boolean extended) { + Log.d(TAG, "start"); - private void changeUserLogViewNewer() { - viewPosition -= PAGE_SIZE; - if (viewPosition < 0) viewPosition = 0; - buildUserLogView(); - mScrollView.post(new Runnable() { - public void run() { - mScrollView.fullScroll(View.FOCUS_UP); - if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) { - mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonTopRecent.setVisibility(View.GONE); - } - } - }); - } + this.extended = extended; + autoScroll = true; + fabCurrent.hide(); + fabSearch.hide(); + + if (userLogRealm == null) + userLogRealm = Realm.getInstance(UploaderApplication.getUserLogConfiguration()); + + UserLogMessage.getInstance().stale(); + + userLogResults = userLogRealm.where(UserLog.class) + .beginGroup() + .equalTo("flag", UserLogMessage.FLAG.NA.value()) + .or() + .equalTo("flag", extended ? UserLogMessage.FLAG.EXTENDED.value() : UserLogMessage.FLAG.NORMAL.value()) + .endGroup() + .sort("timestamp", Sort.ASCENDING) + .findAll(); + + adapter = new UserLogAdapter(context, userLogResults, true); + realmRecyclerView.setAdapter(adapter); + + userLogResults.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<UserLog>>() { + @Override + public void onChange(@NonNull RealmResults realmResults, OrderedCollectionChangeSet changeSet) { + + if (changeSet != null && adapter != null && realmRecyclerView != null) { + + final int i = changeSet.getInsertions().length; + final int d = changeSet.getDeletions().length; + if (d > 0) { + adapter.setLastAnimPosition(adapter.getLastAnimPosition() - d); + } - private void changeUserLogViewRecent() { - viewPosition = 0; - buildUserLogView(); - mScrollView.post(new Runnable() { - public void run() { - mScrollView.fullScroll(View.FOCUS_DOWN); - if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) { - mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE); - } else { - mTextViewLogButtonTopRecent.setVisibility(View.GONE); + RecyclerView rv = realmRecyclerView.getRecycleView(); + + int r = rv.computeVerticalScrollRange(); + int o = rv.computeVerticalScrollOffset(); + int e = rv.computeVerticalScrollExtent(); + + if (autoScroll && (r - o - e < e / 2)) { + rv.post(new Runnable() { + public void run() { + try { + if (d - i > 2) + realmRecyclerView.scrollToPosition(adapter.getItemCount() - 1); + else + realmRecyclerView.smoothScrollToPosition(adapter.getItemCount() - 1); + } catch (Exception ignored) { + } + } + }); + } + + } } - } - }); + }); + } + } + } diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java index 743a5ff183e47c7b641cd3255f7af12d34fd04dc..329cdf0629def3602dcb477955ae3665285fe409 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java @@ -475,7 +475,7 @@ public class MedtronicCnlReader { long starttime = System.currentTimeMillis(); long retrytime = 30000L; - String error = ""; + StringBuilder sb = new StringBuilder(); int attempt = 0; @@ -488,29 +488,31 @@ public class MedtronicCnlReader { } catch (UnexpectedMessageException e) { attempt++; - Log.e(TAG, "Attempt " + attempt + ": UnexpectedMessageException: " + e.getMessage()); - error += "\nAttempt " + attempt + ": UnexpectedMessageException: " + e.getMessage(); + String error = String.format("Attempt %s: UnexpectedMessageException: %s", attempt, e.getMessage()); + Log.e(TAG, error); + sb.append("\n").append(error); // needs to end immediately on these errors if (e.getMessage().contains("connection lost") || e.getMessage().contains("NAK")) { - throw new UnexpectedMessageException(error); + throw new UnexpectedMessageException(sb.toString()); } if (System.currentTimeMillis() - starttime >= retrytime) - throw new UnexpectedMessageException(error); + throw new UnexpectedMessageException(sb.toString()); } catch (TimeoutException e) { attempt++; - Log.e(TAG, "Attempt " + attempt + ": TimeoutException: " + e.getMessage()); - error += "\nAttempt " + attempt + ": TimeoutException: " + e.getMessage(); + String error = String.format("Attempt %s: TimeoutException: %s", attempt, e.getMessage()); + Log.e(TAG, error); + sb.append("\n").append(error); // needs to end immediately on these errors if (e.getMessage().contains("Timeout waiting for 0x81 response")) { - throw new TimeoutException(error); + throw new TimeoutException(sb.toString()); } if (System.currentTimeMillis() - starttime >= retrytime) - throw new TimeoutException(error); + throw new TimeoutException(sb.toString()); } } while (attempt > 0); diff --git a/app/src/main/java/info/nightscout/android/medtronic/Stats.java b/app/src/main/java/info/nightscout/android/medtronic/Stats.java index 6689ddec56b163b0ffe2d920b9ad4af02849cfab..5747a8388ef60b1d08f17884374ace17196e32c3 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/Stats.java +++ b/app/src/main/java/info/nightscout/android/medtronic/Stats.java @@ -7,6 +7,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import info.nightscout.android.UploaderApplication; import info.nightscout.android.model.store.StatCnl; @@ -26,7 +27,7 @@ public class Stats { public static final Class[] STAT_CLASSES = new Class[] {StatPoll.class, StatCnl.class, StatNightscout.class, StatPushover.class}; private static final long STAT_STALE = 30 * 24 * 60 * 60000L; - public static final SimpleDateFormat sdfDateToKey = new SimpleDateFormat("yyyyMMdd"); + public static final SimpleDateFormat sdfDateToKey = new SimpleDateFormat("yyyyMMdd", Locale.US); private int open; private List<LoadedRecord> loadedRecords = new ArrayList<>(); diff --git a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java index 23d36a859a14e67e2443a8176396198119076ac0..1069623f474ac2dc381e19b278e5e1a35018e77a 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java +++ b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java @@ -434,9 +434,11 @@ public class StatusNotification { text += "Basal: suspended"; RealmResults<PumpHistoryBasal> suspend = historyRealm.where(PumpHistoryBasal.class) .greaterThan("eventDate", new Date(now - (12 * 60 * 60000L))) + .beginGroup() .equalTo("recordtype", PumpHistoryBasal.RECORDTYPE.SUSPEND.value()) .or() .equalTo("recordtype", PumpHistoryBasal.RECORDTYPE.RESUME.value()) + .endGroup() .sort("eventDate", Sort.DESCENDING) .findAll(); // check if most recent suspend is in history and show the start time diff --git a/app/src/main/java/info/nightscout/android/medtronic/UserLogAdapter.java b/app/src/main/java/info/nightscout/android/medtronic/UserLogAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..6df08fa5d59489aa6aff972d442e040f163b8149 --- /dev/null +++ b/app/src/main/java/info/nightscout/android/medtronic/UserLogAdapter.java @@ -0,0 +1,226 @@ +package info.nightscout.android.medtronic; + +import android.content.Context; +import android.graphics.Typeface; +import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.AbsoluteSizeSpan; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.ImageSpan; +import android.text.style.StyleSpan; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AlphaAnimation; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.mikepenz.iconics.IconicsDrawable; + +import info.nightscout.android.R; +import info.nightscout.android.model.store.UserLog; +import info.nightscout.android.utils.FormatKit; +import io.realm.RealmBasedRecyclerViewAdapter; +import io.realm.RealmResults; +import io.realm.RealmViewHolder; + +public class UserLogAdapter + extends RealmBasedRecyclerViewAdapter<UserLog, UserLogAdapter.ViewHolder> { + + private final static int FADE_DURATION_MS = 400; + + private final static int cHIGHLIGHT = 0xFFE0E0E0; + private final static int cCLOCK = 0xFFA0A0A0; + private final static int cWARN = 0xFFE0E000; + private final static int cCGM = 0xFFCCA3A3; + private final static int cESTIMATE = 0xFFB7DAE5; + private final static int cHISTORY = 0xFFABAFCC; + private final static int cHEART = 0xFFFF0000; + + private IconicsDrawable iWARN; + private IconicsDrawable iINFO; + private IconicsDrawable iNOTE; + private IconicsDrawable iHELP; + private IconicsDrawable iREFRESH; + private IconicsDrawable iCGM; + private IconicsDrawable iSETTING; + private IconicsDrawable iHEART; + private IconicsDrawable iSHARE; + + private int cDefault; + private int iBounds; + private int iOffsetXDp; + private int iOffsetYDp; + + private int lastAnimPosition = -1; + + private Context mContext; + + public UserLogAdapter( + Context context, + RealmResults<UserLog> realmResults, + boolean automaticUpdate) { + super(context, realmResults, automaticUpdate, false); + + mContext = context; + } + + @Override + public ViewHolder onCreateRealmViewHolder(@NonNull ViewGroup viewGroup, int viewType) { + View v = inflater.inflate(R.layout.log_item, viewGroup, false); + ViewHolder vh = new ViewHolder((FrameLayout) v); + return vh; + } + + @Override + public void onBindRealmViewHolder(@NonNull ViewHolder viewHolder, int position) { + final UserLog userLog = realmResults.get(position); + + setContent(viewHolder.textView, userLog); + + if (position > lastAnimPosition) { + setFadeAnimation(viewHolder.itemView); + lastAnimPosition = position; + } + } + + public class ViewHolder extends RealmViewHolder { + + public TextView textView; + + public ViewHolder(FrameLayout container) { + super(container); + this.textView = container.findViewById(R.id.log_textview); + } + } + + public int getLastAnimPosition() { + return lastAnimPosition; + } + + public void setLastAnimPosition(int lastAnimPosition) { + this.lastAnimPosition = lastAnimPosition; + } + + private void setFadeAnimation(View view) { + AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(FADE_DURATION_MS); + view.startAnimation(anim); + } + + private void setContent(TextView tv, UserLog ul) { + + if (iWARN == null) initIcons(tv); + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + + UserLogMessage.TYPE type = UserLogMessage.TYPE.convert(ul.getType()); + String clock = FormatKit.getInstance().formatAsDayClockSeconds(ul.getTimestamp()); + String text = ul.getMessageParsed(); + + switch (type) { + case WARN: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iWARN, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cWARN), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case HELP: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iHELP, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case INFO: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iINFO, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case NOTE: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iNOTE, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case HISTORY: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iREFRESH, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case CGM: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iCGM, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cCGM), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case OPTION: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iSETTING, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case HEART: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iHEART, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case SGV: + ssb.append(" ").append(text); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 1, text.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case ESTIMATE: + ssb.append(" ").append(text); + ssb.setSpan(new ForegroundColorSpan(cESTIMATE), 1, text.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case REQUESTED: + case RECEIVED: + ssb.append(" ").append(text); + ssb.setSpan(new ForegroundColorSpan(cHISTORY), 1, text.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case SHARE: + ssb.append(" * ").append(text); + ssb.setSpan(new ImageSpan(iSHARE, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new ForegroundColorSpan(cHIGHLIGHT), 3, text.length() + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + default: + ssb.append(" ").append(text); + } + + ssb.insert(0, clock); + ssb.setSpan(new ForegroundColorSpan(cCLOCK), 0, clock.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.setSpan(new AbsoluteSizeSpan(11, true), 0, clock.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + tv.setText(ssb); + } + + private void initIcons(TextView tv) { + + cDefault = tv.getCurrentTextColor(); + iBounds = (int) (tv.getTextSize() * 1.2); + iOffsetXDp = 0; + iOffsetYDp = 3; + + iWARN = icon("ion_alert_circled", cWARN); + iINFO = icon("ion_information_circled", cHIGHLIGHT); + //iNOTE = icon("ion_clipboard", cHIGHLIGHT); + iNOTE = icon("ion_document", cHIGHLIGHT); + //iNOTE = icon("ion_stats_bars", cHIGHLIGHT); + iHELP = icon("ion_ios_lightbulb", cHIGHLIGHT); + iCGM = icon("ion_ios_pulse_strong", cCGM); + iHEART = icon("ion_heart", cHEART); + iSHARE = icon("ion_android_share_alt", cHIGHLIGHT); + //iREFRESH = icon("ion_loop", cDefault); + iREFRESH = icon("ion_refresh", cDefault); + iSETTING = icon("ion_android_settings", cHIGHLIGHT); + } + + private IconicsDrawable icon(String icon, int color) { + + IconicsDrawable iconicsDrawable = new IconicsDrawable(mContext) + .icon(icon) + .iconOffsetXDp(iOffsetXDp) + .iconOffsetYDp(iOffsetYDp) + .color(color); + + iconicsDrawable.setBounds(0,0, iBounds, iBounds); + + return iconicsDrawable; + } + +} diff --git a/app/src/main/java/info/nightscout/android/medtronic/UserLogMessage.java b/app/src/main/java/info/nightscout/android/medtronic/UserLogMessage.java index 6cbb960feb4196ead67a69f5e2baacc795463bb3..7969b358bebb648434e99d1e603b707743aaca80 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/UserLogMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/UserLogMessage.java @@ -1,9 +1,12 @@ package info.nightscout.android.medtronic; +import android.content.Context; +import android.content.Intent; import android.support.annotation.NonNull; import android.util.Log; import info.nightscout.android.UploaderApplication; +import info.nightscout.android.medtronic.service.MasterService; import info.nightscout.android.model.store.UserLog; import io.realm.Realm; import io.realm.RealmResults; @@ -12,81 +15,226 @@ public class UserLogMessage { private static final String TAG = UserLogMessage.class.getSimpleName(); private static UserLogMessage instance; - private static final long STALE_MS = 72 * 60 * 60 * 1000L; + private static final long STALE_MS = 7 * 24 * 60 * 60000L; private UserLogMessage() { + Log.d(TAG, "UserLogMessage: init called [Pid=" + android.os.Process.myPid() + "]"); + } + + private static class LazyHolder { + static final UserLogMessage instance = new UserLogMessage(); } public static UserLogMessage getInstance() { - if (UserLogMessage.instance == null) { - instance = new UserLogMessage(); - } - return instance; + return LazyHolder.instance; } - private void addMessage(final String message) { + private void addMessage(final boolean async, final long timestamp, final TYPE type, final FLAG flag, final String message) { Log.d(TAG, "addMessage: " + message); Realm userLogRealm = Realm.getInstance(UploaderApplication.getUserLogConfiguration()); try { - userLogRealm.executeTransactionAsync(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { + if (async) { + userLogRealm.executeTransactionAsync(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + realm.copyToRealmOrUpdate(new UserLog().message(timestamp, type.value(), flag.value(), message)); + } + }); + } else { + userLogRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + realm.copyToRealmOrUpdate(new UserLog().message(timestamp, type.value(), flag.value(), message)); + } + }); + } + } catch (Exception e) { + // rare, can throw when there are too many messages to be handled using concurrent async realm processes + Log.e(TAG, "Could not add message: " + e.getMessage()); + } + + userLogRealm.close(); + } - realm.createObject(UserLog.class).UserLogMessage(message); + public void stale() { + Realm userLogRealm = Realm.getInstance(UploaderApplication.getUserLogConfiguration()); + try { + userLogRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { // remove stale items RealmResults results = realm.where(UserLog.class) .lessThan("timestamp", System.currentTimeMillis() - STALE_MS) .findAll(); if (results.size() > 0) results.deleteAllFromRealm(); + Log.d(TAG, String.format("removed %s stale items", results.size())); } }); } catch (Exception e) { - // rare, can throw when there are too many messages to be handled using concurrent async realm processes - Log.e(TAG, "Could not add message"); + Log.e(TAG, "Could not remove stale messages: " + e.getMessage()); } userLogRealm.close(); } - public void add(String message) { - addMessage(message); - } - public void clear() { Realm userLogRealm = Realm.getInstance(UploaderApplication.getUserLogConfiguration()); - userLogRealm.executeTransactionAsync(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { - realm.deleteAll(); - } - }); + try { + userLogRealm.executeTransactionAsync(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + realm.deleteAll(); + } + }); + } catch (Exception e) { + Log.e(TAG, "Could not clear messages: " + e.getMessage()); + } userLogRealm.close(); } - public final class Icons { - // TODO - use a message type and insert icon as part of ui user log message handling - public static final String ICON_WARN = "{ion_alert_circled} "; - public static final String ICON_BGL = "{ion_waterdrop} "; - public static final String ICON_USB = "{ion_usb} "; - public static final String ICON_INFO = "{ion_information_circled} "; - public static final String ICON_HELP = "{ion_ios_lightbulb} "; - public static final String ICON_SETTING = "{ion_android_settings} "; - public static final String ICON_HEART = "{ion_heart} "; - public static final String ICON_LOW = "{ion_battery_low} "; - public static final String ICON_FULL = "{ion_battery_full} "; - public static final String ICON_CGM = "{ion_ios_pulse_strong} "; - public static final String ICON_SUSPEND = "{ion_pause} "; - public static final String ICON_RESUME = "{ion_play} "; - public static final String ICON_BOLUS = "{ion_skip_forward} "; - public static final String ICON_BASAL = "{ion_skip_forward} "; - public static final String ICON_CHANGE = "{ion_android_hand} "; - public static final String ICON_REFRESH = "{ion_loop} "; - public static final String ICON_BELL = "{ion_android_notifications} "; - public static final String ICON_NOTE = "{ion_clipboard} "; + public void add(String message) { + add(TYPE.NA, FLAG.NA, message); + } + + public void add(TYPE type, String message) { + add(type, FLAG.NA, message); + } + + public void add(int id) { + add(TYPE.NA, FLAG.NA, String.format("{id;%s}", id)); + } + + public void add(TYPE type, int id) { + add(type, String.format("{id;%s}", id)); + } + + public void add(TYPE type, FLAG flag, String message) { + addMessage(false, System.currentTimeMillis(), type, flag, message); + } + + public void addAsync(TYPE type, FLAG flag, String message) { + addMessage(true, System.currentTimeMillis(), type, flag, message); + } + + public static void send(Context context, String message) { + send(context, TYPE.NA, FLAG.NA, message); } + + public static void send(Context context, TYPE type, String message) { + send(context, type, FLAG.NA, message); + } + + public static void send(Context context, int id) { + send(context, TYPE.NA, FLAG.NA, String.format("{id;%s}", id)); + } + + public static void send(Context context, TYPE type, int id) { + send(context, type, FLAG.NA, String.format("{id;%s}", id)); + } + + public static void sendN(Context context, String message) { + send(context, TYPE.NA, FLAG.NORMAL, message); + } + + public static void sendN(Context context, TYPE type, String message) { + send(context, type, FLAG.NORMAL, message); + } + + public static void sendN(Context context, TYPE type, int id) { + send(context, type, FLAG.NORMAL, String.format("{id;%s}", id)); + } + + public static void sendE(Context context, String message) { + send(context, TYPE.NA, FLAG.EXTENDED, message); + } + + public static void sendE(Context context, TYPE type, String message) { + send(context, type, FLAG.EXTENDED, message); + } + + public static void sendE(Context context, TYPE type, int id) { + send(context, type, FLAG.EXTENDED, String.format("{id;%s}", id)); + } + + private static void send(Context context, TYPE type, FLAG flag, String message) { + try { + Intent intent = + new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) + .putExtra("type", type) + .putExtra("flag", flag) + .putExtra("message", message); + context.sendBroadcast(intent); + } catch (Exception ignored) { + } + } + + public enum TYPE { + WARN(1), + INFO(2), + HELP(3), + OPTION(4), + CGM(5), + SGV(6), + HISTORY(7), + HEART(8), + ESTIMATE(9), + NOTE(10), + REQUESTED(11), + RECEIVED(12), + SHARE(13), + NA(-1); + + private int value; + + TYPE(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + + public boolean equals(int value) { + return this.value == value; + } + + public static TYPE convert(int value) { + for (TYPE type : TYPE.values()) + if (type.value == value) return type; + return TYPE.NA; + } + } + + public enum FLAG { + NORMAL(1), + EXTENDED(2), + DEBUG(3), + NA(-1); + + private int value; + + FLAG(int value) { + this.value = value; + } + + public byte value() { + return (byte) this.value; + } + + public boolean equals(int value) { + return this.value == value; + } + + public static FLAG convert(byte value) { + for (FLAG flag : FLAG.values()) + if (flag.value == value) return flag; + return FLAG.NA; + } + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java index 381177729b296ba03a3e3a95b81dcc1fb4b59fd6..27e1e831ff28ebb025c173614483c3a0337d7b39 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java @@ -286,7 +286,7 @@ public abstract class ContourNextLinkMessage { if (bytesRead == -1) { if (runtime > 10000) Log.w(TAG, "READ: runtime > 10000ms TIMEOUT" + info); else Log.d(TAG, "READ: TIMEOUT" + info); - throw new TimeoutException("Timeout waiting for response from pump"); + throw new TimeoutException("Timeout waiting for a read response " + info); } // a 'response divisible by 60' is in general a valid response on a block boundary, noted in log as it may also be due to a usb read error @@ -295,8 +295,6 @@ public abstract class ContourNextLinkMessage { if (runtime > 10000) Log.w(TAG, "READ: runtime > 10000ms" + info); - else if (runtime > 21000) - Log.w(TAG, "READ: !!! runtime > 21000ms !!!" + info); if (DEBUG_READ) Log.d(TAG, "READ:" + info + HexDump.dumpHexString(responseMessage.toByteArray())); diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java index e3599125376105301ce4d488aea051de12707a5a..c98e0448c8edd3a5da8d27f614390e0f7c75cd59 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java @@ -33,8 +33,6 @@ public abstract class ContourNextLinkRequestMessage<T> extends ContourNextLinkMe public T send(UsbHidDriver mDevice, int millis) throws UnexpectedMessageException, EncryptionException, TimeoutException, ChecksumException, IOException { - //clearMessage(mDevice, PRESEND_CLEAR_TIMEOUT_MS); - sendMessage(mDevice); if (millis > 0) { try { @@ -50,8 +48,6 @@ public abstract class ContourNextLinkRequestMessage<T> extends ContourNextLinkMe public T send(UsbHidDriver mDevice, int millis, int timeout) throws UnexpectedMessageException, EncryptionException, TimeoutException, ChecksumException, IOException { - //clearMessage(mDevice, PRESEND_CLEAR_TIMEOUT_MS); - sendMessage(mDevice); if (millis > 0) { try { diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java index 26a70668bbbac85c00519a52a03e2ee923ad58bf..ab207f965a6e35664be59ebe4be3e558af94cf63 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java @@ -23,10 +23,7 @@ import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.util.Log; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Locale; import info.nightscout.android.history.PumpHistoryHandler; import info.nightscout.android.medtronic.Stats; @@ -42,8 +39,9 @@ import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.model.store.DataStore; import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.upload.nightscout.NightscoutUploadService; +import info.nightscout.android.utils.RealmKit; import info.nightscout.android.xdrip_plus.XDripPlusUploadService; -import info.nightscout.urchin.Urchin; +import info.nightscout.android.urchin.Urchin; import io.realm.Realm; import io.realm.RealmList; import io.realm.RealmResults; @@ -51,9 +49,6 @@ import io.realm.Sort; import static android.support.v4.app.NotificationCompat.PRIORITY_MAX; import static android.support.v4.app.NotificationCompat.VISIBILITY_PUBLIC; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_HELP; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_INFO; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_WARN; import static info.nightscout.android.medtronic.service.MedtronicCnlService.POLL_GRACE_PERIOD_MS; import static info.nightscout.android.medtronic.service.MedtronicCnlService.POLL_PERIOD_MS; import static info.nightscout.android.medtronic.service.MedtronicCnlService.POLL_PRE_GRACE_PERIOD_MS; @@ -77,13 +72,12 @@ public class MasterService extends Service { private Realm storeRealm; private DataStore dataStore; - private MasterServiceReceiver masterServiceReceiver = new MasterServiceReceiver(); - private UserLogMessageReceiver userLogMessageReceiver = new UserLogMessageReceiver(); - private BatteryReceiver batteryReceiver = new BatteryReceiver(); - private UsbReceiver usbReceiver = new UsbReceiver(); + private MasterServiceReceiver masterServiceReceiver; + private UserLogMessageReceiver userLogMessageReceiver; + private BatteryReceiver batteryReceiver; + private UsbReceiver usbReceiver; - private UserLogMessage userLogMessage = UserLogMessage.getInstance(); - private StatusNotification statusNotification = StatusNotification.getInstance(); + private StatusNotification statusNotification; private Urchin urchin; @@ -130,8 +124,6 @@ public class MasterService extends Service { private boolean serviceActive = true; - private DateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss", Locale.US); - @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); @@ -141,10 +133,11 @@ public class MasterService extends Service { public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate called"); - //userLogMessage.add(TAG + " onCreate called"); mContext = this.getBaseContext(); + RealmKit.compact(mContext); + storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration()); dataStore = storeRealm.where(DataStore.class).findFirst(); @@ -154,12 +147,19 @@ public class MasterService extends Service { storeRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(@NonNull Realm realm) { + dataStore.clearAllCommsErrors(); + // check Xdrip available + dataStore.setXdripPlusUploadAvailable(false); + // check Nightscout site available dataStore.setNightscoutReportTime(0); dataStore.setNightscoutAvailable(false); - dataStore.clearAllCommsErrors(); + // revalidate Pushover account + dataStore.setPushoverAPItokenCheck(""); + dataStore.setPushoverUSERtokenCheck(""); } }); + masterServiceReceiver = new MasterServiceReceiver(); IntentFilter masterServiceIntentFilter = new IntentFilter(); masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_ACTIVE); masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_FINISHED); @@ -170,16 +170,19 @@ public class MasterService extends Service { masterServiceIntentFilter.addAction(Constants.ACTION_ALARM_RECEIVED); registerReceiver(masterServiceReceiver, masterServiceIntentFilter); + userLogMessageReceiver = new UserLogMessageReceiver(); registerReceiver( userLogMessageReceiver, new IntentFilter(Constants.ACTION_USERLOG_MESSAGE)); + batteryReceiver = new BatteryReceiver(); IntentFilter batteryIntentFilter = new IntentFilter(); batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW); batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY); registerReceiver(batteryReceiver, batteryIntentFilter); + usbReceiver = new UsbReceiver(); IntentFilter usbIntentFilter = new IntentFilter(); usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); @@ -188,6 +191,8 @@ public class MasterService extends Service { registerReceiver(usbReceiver, usbIntentFilter); urchin = new Urchin(mContext); + + statusNotification = StatusNotification.getInstance(); } } @@ -324,11 +329,13 @@ public class MasterService extends Service { stopSelf(); } else if (Constants.ACTION_READ_NOW.equals(action)) { + UserLogMessage.send(mContext, R.string.main_requesting_poll_now); cancelAlarm(); startService(new Intent(mContext, MedtronicCnlService.class) .setAction(MasterService.Constants.ACTION_CNL_READPUMP)); } else if (Constants.ACTION_READ_PROFILE.equals(action)) { + UserLogMessage.send(mContext, R.string.main_requesting_pump_profile); storeRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(@NonNull Realm realm) { @@ -342,14 +349,14 @@ public class MasterService extends Service { } else if (Constants.ACTION_URCHIN_UPDATE.equals(action)) { urchin.update(); - +/* } else if (Constants.ACTION_ALARM_RECEIVED.equals(action)) { long time = intent.getLongExtra("time", 0); Log.d(TAG, "Received alarm broadcast message at " + new Date(time)); userLogMessage.add("* alarm: " + new Date(time)); startService(new Intent(mContext, MedtronicCnlService.class) .setAction(MasterService.Constants.ACTION_CNL_READPUMP)); - +*/ } } @@ -406,7 +413,7 @@ public class MasterService extends Service { statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL, start); if (start - now > 10000L) - userLogMessage.add("Next poll due at: " + dateFormatter.format(start)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, String.format("{id;%s} {time.poll;%s}", R.string.next_poll_due_at, start)); } private long nextPollTime() { @@ -480,9 +487,10 @@ public class MasterService extends Service { private class UserLogMessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - String message = intent.getStringExtra(Constants.EXTENDED_DATA); - Log.i(TAG, "Message Receiver: " + message); - userLogMessage.add(message); + UserLogMessage.getInstance().addAsync( + (UserLogMessage.TYPE) intent.getSerializableExtra("type"), + (UserLogMessage.FLAG) intent.getSerializableExtra("flag"), + intent.getStringExtra("message")); } } @@ -506,7 +514,7 @@ public class MasterService extends Service { if (Constants.ACTION_HAS_USB_PERMISSION.equals(action)) { if (hasUsbPermission()) { - userLogMessage.add(ICON_INFO + "Got permission for USB."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, "Got permission for USB."); statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL); startCgmServiceDelayed(MedtronicCnlService.USB_WARMUP_TIME_MS); } @@ -514,7 +522,7 @@ public class MasterService extends Service { // received from OS } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { Log.d(TAG, "USB plugged in"); - userLogMessage.add(ICON_INFO + "Contour Next Link plugged in."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, "Contour Next Link plugged in."); clearDisconnectionNotification(); storeRealm.executeTransaction(new Realm.Transaction() { @@ -533,14 +541,14 @@ public class MasterService extends Service { } else { Log.d(TAG, "No permission for USB. Waiting."); - userLogMessage.add(ICON_INFO + "Waiting for USB permission."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, "Waiting for USB permission."); } // received from OS } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { Log.d(TAG, "USB unplugged"); showDisconnectionNotification("USB Error", "Contour Next Link unplugged."); - userLogMessage.add(ICON_WARN + "USB error. Contour Next Link unplugged."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, "USB error. Contour Next Link unplugged."); statusNotification.updateNotification(StatusNotification.NOTIFICATION.ERROR); @@ -550,14 +558,14 @@ public class MasterService extends Service { // received from CnlService } else if (Constants.ACTION_NO_USB_PERMISSION.equals(action)) { Log.d(TAG, "No permission to read the USB device."); - userLogMessage.add(ICON_WARN + "No permission to read the USB device."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, "No permission to read the USB device."); statusNotification.updateNotification(StatusNotification.NOTIFICATION.ERROR); if (dataStore.isSysEnableUsbPermissionDialog()) { requestUsbPermission(); } else { - userLogMessage.add(ICON_HELP + "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent."); } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java index 0e133352b14bd92658657ea6237dd87da6bc5c39..bc0f5a0ffd8b439942acf3682756649767fb8132 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java @@ -32,6 +32,7 @@ import info.nightscout.android.medtronic.MedtronicCnlReader; import info.nightscout.android.history.PumpHistoryHandler; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.medtronic.Stats; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.medtronic.exception.ChecksumException; import info.nightscout.android.medtronic.exception.EncryptionException; import info.nightscout.android.medtronic.exception.UnexpectedMessageException; @@ -44,18 +45,11 @@ import info.nightscout.android.model.medtronicNg.PumpInfo; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.model.store.DataStore; import info.nightscout.android.model.store.StatPoll; -import info.nightscout.android.utils.FormatKit; import info.nightscout.android.utils.HexDump; import io.realm.Realm; import io.realm.RealmResults; import io.realm.Sort; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_CGM; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_HELP; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_INFO; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_REFRESH; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_SETTING; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_WARN; import static info.nightscout.android.utils.ToolKit.getWakeLock; import static info.nightscout.android.utils.ToolKit.releaseWakeLock; @@ -119,18 +113,6 @@ public class MedtronicCnlService extends Service { private int pumpClockError; private int pumpBatteryError; - private DateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss", Locale.US); - - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - sendBroadcast(intent); - } catch (Exception ignored) { - } - } - protected void sendMessage(String action) { try { Intent intent = @@ -341,16 +323,19 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received long due = checkPollTime(); if (due > 0) { if (dataStore.isSysEnableClashProtect()) { - userLogMessage(String.format(Locale.getDefault(), - getString(R.string.please_wait) + ": " + getString(R.string.pump_is_expecting_sensor_communication), - (due - System.currentTimeMillis()) / 1000L)); + UserLogMessage.send(mContext, String.format("{id;%s}: {id;%s} %s {id;%s}.", + R.string.please_wait, + R.string.pump_is_expecting_sensor_communication, + (due - System.currentTimeMillis()) / 1000L, + R.string.time_seconds)); nextpoll = due; return; } else { - userLogMessage(String.format(Locale.getDefault(), - getString(R.string.pump_is_expecting_sensor_communication), - (due - System.currentTimeMillis()) / 1000L)); - userLogMessage(ICON_SETTING + getString(R.string.radio_clash_protection_is_disabled)); + UserLogMessage.send(mContext, String.format("{id;%s} %s {id;%s}.", + R.string.pump_is_expecting_sensor_communication, + (due - System.currentTimeMillis()) / 1000L, + R.string.time_seconds)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.OPTION, R.string.radio_clash_protection_is_disabled); } } @@ -361,7 +346,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received try { Log.d(TAG, "Connecting to Contour Next Link [pid" + android.os.Process.myPid() + "]"); - userLogMessage(getString(R.string.connecting_to_contour_next_link)); + UserLogMessage.send(mContext, R.string.connecting_to_contour_next_link); shutdownProtect = true; cnlReader.requestDeviceInfo(); @@ -419,7 +404,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received final byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel()); if (radioChannel == 0) { Log.i(TAG, "Could not communicate with the pump. Is it nearby?"); - userLogMessage(ICON_WARN + getString(R.string.could_not_communicate_with_the_pump)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.could_not_communicate_with_the_pump); statPoll.incPollNoConnect(); commsConnectError++; pollInterval = pollInterval / (dataStore.isDoublePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available @@ -427,9 +412,12 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received pumpHistoryHandler.systemStatus(PumpHistorySystem.STATUS.COMMS_PUMP_LOST); } else if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 5) { - userLogMessage(String.format(Locale.getDefault(), "Connected on channel %d RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage())); - userLogMessage(ICON_WARN + "Warning: pump signal too weak. Is it nearby?"); Log.i(TAG, "Warning: pump signal too weak. Is it nearby?"); + UserLogMessage.send(mContext, String.format("{id;%s} %s RSSI: %s%%", + R.string.connected_on_channel, + radioChannel, + cnlReader.getPumpSession().getRadioRSSIpercentage())); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, "Warning: pump signal too weak. Is it nearby?"); commsConnectError++; commsSignalError++; pollInterval = POLL_PERIOD_MS / (dataStore.isDoublePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available @@ -444,8 +432,10 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received statPoll.setPollRSSI(statPoll.getPollRSSI() + cnlReader.getPumpSession().getRadioRSSIpercentage()); Log.d(TAG, String.format("Connected on channel %d RSSI: %d%%", radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage())); - userLogMessage(String.format(Locale.getDefault(), getString(R.string.connected_on_channel_rssi), - radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage())); + UserLogMessage.send(mContext, String.format("{id;%s} %s RSSI: %s%%", + R.string.connected_on_channel, + radioChannel, + cnlReader.getPumpSession().getRadioRSSIpercentage())); // read pump status final PumpStatusEvent pumpRecord = new PumpStatusEvent(); @@ -524,7 +514,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (!pumpRecord.isOldSgvWhenNewExpected() && !(pumpBatteryError > 0 && dataStore.getLowBatPollInterval() > POLL_PERIOD_MS - && pumpHistoryHandler.pumpHistoryRecency() < 60 * 60000L)) { + && pumpHistoryHandler.pumpHistoryRecency() < 60 * 60000L)) { if (dataStore.isRequestProfile()) { pumpHistoryHandler.profile(cnlReader); @@ -562,38 +552,34 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (nak == ContourNextLinkMessage.NAK.DEVICE_HAS_ERROR) { pumpHistoryHandler.systemStatus(PumpHistorySystem.STATUS.PUMP_DEVICE_ERROR); - userLogMessage(ICON_WARN + "Pump has a device error. No data can be read until this error has been cleared on the Pump."); - } else if (dataStore.isDbgEnableExtendedErrors()) { - userLogMessage(ICON_WARN + getString(R.string.error_communication) + " " + e.getMessage()); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + "Pump has a device error. No data can be read until this error has been cleared on the Pump."); } else { - userLogMessage(ICON_WARN + getString(R.string.error_communication_busy_noisy)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_communication_busy_noisy); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s}: %s", R.string.error_communication, e.getMessage())); } + } catch (TimeoutException e) { commsError++; pollInterval = dataStore.isSysEnablePollOverride() ? dataStore.getSysPollErrorRetry() : POLL_ERROR_RETRY_MS; Log.e(TAG, "Timeout communicating with the Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_timeout) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_timeout_pump)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_timeout_pump); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_timeout, e.getMessage())); } catch (ChecksumException e) { commsError++; Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_checksum) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_checksum_cnl)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_checksum_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_checksum, e.getMessage())); } catch (EncryptionException e) { commsError++; Log.e(TAG, "Error decrypting messages from Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_decryption) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_decryption_cnl)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_decryption_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_decryption, e.getMessage())); } catch (NoSuchAlgorithmException e) { commsError++; Log.e(TAG, "Could not determine CNL HMAC", e); - userLogMessage(ICON_WARN + getString(R.string.error_hashing)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_hashing); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_hashing, e.getMessage())); } finally { try { cnlReader.closeConnection(); @@ -607,52 +593,40 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received } catch (IOException e) { commsError++; Log.e(TAG, "Error connecting to Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - //userLogMessage(ICON_WARN + getString(R.string.error_connecting_cnl) + " " + e.getMessage()); - userLogMessage(ICON_WARN + getString(R.string.error_connecting_cnl) + " " + e.getMessage() + "\n" + Log.getStackTraceString(e)); - else - userLogMessage(ICON_WARN + getString(R.string.error_connecting_cnl)); - if (cnlReader.resetCNL()) userLogMessage(ICON_INFO + getString(R.string.error_cnl_reset_success)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_connecting_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_connecting_cnl, e.getMessage())); + if (cnlReader.resetCNL()) UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, R.string.error_cnl_reset_success); } catch (ChecksumException e) { commsError++; Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_checksum) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_checksum_cnl)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_checksum_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_checksum, e.getMessage())); } catch (EncryptionException e) { commsError++; Log.e(TAG, "Error decrypting messages from Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_decryption) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_decryption_cnl)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_decryption_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_decryption, e.getMessage())); } catch (TimeoutException e) { commsError++; Log.e(TAG, "Timeout communicating with the Contour Next Link.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_timeout) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_timeout_cnl)); - if (cnlReader.resetCNL()) userLogMessage(ICON_INFO + getString(R.string.error_cnl_reset_success)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_timeout_cnl); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_timeout, e.getMessage())); + if (cnlReader.resetCNL()) UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, R.string.error_cnl_reset_success); } catch (UnexpectedMessageException e) { commsError++; Log.e(TAG, "Could not close connection.", e); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.error_close_connection) + " " + e.getMessage()); - else - userLogMessage(ICON_WARN + getString(R.string.error_close_connection)); - if (cnlReader.resetCNL()) userLogMessage(ICON_INFO + getString(R.string.error_cnl_reset_success)); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.error_close_connection); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.error_close_connection, e.getMessage())); + if (cnlReader.resetCNL()) UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, R.string.error_cnl_reset_success); } finally { shutdownProtect = false; nextpoll = requestPollTime(timePollStarted, pollInterval); long timer = System.currentTimeMillis() - timePollStarted; - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(String.format(Locale.getDefault(), getString(R.string.next_poll_due_at) + " [%dms]", - dateFormatter.format(nextpoll), timer)); - else - userLogMessage(String.format(Locale.getDefault(), getString(R.string.next_poll_due_at), dateFormatter.format(nextpoll))); + + UserLogMessage.sendN(mContext, String.format("{id;%s} {time.poll;%s}", R.string.next_poll_due_at, nextpoll)); + UserLogMessage.sendE(mContext, String.format("{id;%s} {time.poll.e;%s} [%sms]", R.string.next_poll_due_at, nextpoll, timer)); + statPoll.timer(timer); RemoveOutdatedRecords(); @@ -661,13 +635,9 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received } catch (Exception e) { Log.e(TAG, "Unexpected Error! " + Log.getStackTraceString(e)); - try { - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage(ICON_WARN + getString(R.string.unexpected_error) + "\n" + Log.getStackTraceString(e)); - } catch (Exception ignored) {} - int retry = 60; - userLogMessage(String.format(Locale.getDefault(), getString(R.string.polling_service_could_not_complete), retry)); - nextpoll = System.currentTimeMillis() + (retry * 1000L); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.WARN, R.string.polling_service_could_not_complete); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.WARN, String.format("{id;%s} %s", R.string.unexpected_error, e.getMessage())); + nextpoll = System.currentTimeMillis() + 60000L; } finally { @@ -709,24 +679,26 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received String todayKey = Stats.sdfDateToKey.format(timePollStarted); Log.i(TAG, "STATS: " + Stats.report(todayKey)); - if (dataStore.isDbgEnableExtendedErrors()) { - long yesterday = timePollStarted - 24 * 60 * 60000L; - final String yesterdayKey = Stats.sdfDateToKey.format(yesterday); + long yesterday = timePollStarted - 24 * 60 * 60000L; + final String yesterdayKey = Stats.sdfDateToKey.format(yesterday); - String last = dataStore.getLastStatReport(); - if (last == null || !last.equals(yesterdayKey)) { + String last = dataStore.getLastStatReport(); + if (last == null || !last.equals(yesterdayKey)) { - String report = Stats.report(yesterdayKey); - if (report.length() > 0) - userLogMessage(ICON_INFO + FormatKit.getInstance().formatAsWeekday(yesterday) + " Stats: " + report); + String report = Stats.report(yesterdayKey); + if (report.length() > 0) + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.NOTE, + String.format("{weekday;%s} Stats: %s", + yesterday, + report + )); - storeRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { - dataStore.setLastStatReport(yesterdayKey); - } - }); - } + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + dataStore.setLastStatReport(yesterdayKey); + } + }); } } @@ -770,70 +742,73 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received pumpCgmNA = 0; // poll clash detection pumpLostSensorError = 0; - Log.d(TAG, "&&& CgmWarmUp=" + pumpRecord.isCgmWarmUp() + - " CalibrationDueMinutes=" + pumpRecord.getCalibrationDueMinutes() + - " CgmCalibrating=" + pumpRecord.isCgmCalibrating() + - " ExceptionType=" + pumpRecord.getCgmExceptionType() + - " CgmStatus=" + pumpRecord.getCgmStatus() - ); - if (pumpRecord.isCgmWarmUp()) - userLogMessage(ICON_CGM + "sensor is in warm-up phase"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor is in warm-up phase"); else if (pumpRecord.getCalibrationDueMinutes() == 0) - userLogMessage(ICON_CGM + "sensor calibration is due now!"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor calibration is due now!"); else if (pumpRecord.getSgv() == 0 && pumpRecord.isCgmCalibrating()) - userLogMessage(ICON_CGM + "sensor is calibrating"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor is calibrating"); else if (pumpRecord.getSgv() == 0) switch (PumpHistoryParser.CGM_EXCEPTION.convert(pumpRecord.getCgmExceptionType())) { case SENSOR_INIT: - userLogMessage(ICON_CGM + "sensor error (init)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (init)"); break; case SENSOR_CAL_NEEDED: - userLogMessage(ICON_CGM + "sensor error (cal needed)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (cal needed)"); break; case SENSOR_ERROR: - userLogMessage(ICON_CGM + "sensor error (sgv not available)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (sgv not available)"); break; case SENSOR_CHANGE_SENSOR_ERROR: - userLogMessage(ICON_CGM + "sensor error (change sensor)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (change sensor)"); break; case SENSOR_END_OF_LIFE: - userLogMessage(ICON_CGM + "sensor error (end of life)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (end of life)"); break; case SENSOR_NOT_READY: - userLogMessage(ICON_CGM + "sensor error (not ready)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (not ready)"); break; case SENSOR_READING_HIGH: - userLogMessage(ICON_CGM + "sensor error (reading high)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (reading high)"); break; case SENSOR_READING_LOW: - userLogMessage(ICON_CGM + "sensor error (reading low)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (reading low)"); break; case SENSOR_CAL_PENDING: - userLogMessage(ICON_CGM + "sensor error (cal pending)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (cal pending)"); break; case SENSOR_CAL_ERROR: - userLogMessage(ICON_CGM + "sensor error (cal error)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (cal error)"); break; case SENSOR_TIME_UNKNOWN: - userLogMessage(ICON_CGM + "sensor error (time unknown)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (time unknown)"); break; default: - userLogMessage(ICON_CGM + "sensor error (n/a)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor error (n/a)"); } else { commsSgvSuccess++; - userLogMessage("SGV: ¦" + pumpRecord.getSgv() - + "¦ At: " + dateFormatter.format(pumpRecord.getCgmDate().getTime()) - + " Pump: " + (pumpClockDifference > 0 ? "+" : "") + (pumpClockDifference / 1000L) + "sec"); + UserLogMessage.sendN(mContext, UserLogMessage.TYPE.SGV, + String.format("SGV {sgv;%s} at {time.sgv;%s} Pump {diff;%s}", + pumpRecord.getSgv(), + pumpRecord.getCgmDate().getTime(), + pumpClockDifference / 1000L + )); + UserLogMessage.sendE(mContext, UserLogMessage.TYPE.SGV, + String.format("SGV {sgv;%s} at {time.sgv.e;%s} Pump {diff;%s}", + pumpRecord.getSgv(), + pumpRecord.getCgmDate().getTime(), + pumpClockDifference / 1000L + )); + if (pumpRecord.isCgmCalibrating()) - userLogMessage(ICON_CGM + "sensor is calibrating"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"sensor is calibrating"); } if (pumpRecord.isOldSgvWhenNewExpected()) { - userLogMessage(ICON_CGM + "old cgm event received"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"old cgm event received"); if (!pumpRecord.isCgmWarmUp()) { // pump may have missed sensor transmission or be delayed in posting to status message // in most cases the next scheduled poll will have latest sgv, occasionally it is available this period after a delay @@ -845,46 +820,41 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received pumpCgmNA++; // poll clash detection if (commsSgvSuccess > 0) { pumpLostSensorError++; // only count errors if cgm is being used - userLogMessage(ICON_CGM + "cgm n/a (pump lost sensor)"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"cgm n/a (pump lost sensor)"); } else { - userLogMessage(ICON_CGM + "cgm n/a"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.CGM,"cgm n/a"); } } } private void statusWarnings() { -/* - if (pumpHistoryHandler.isLoopActive()) { - userLogMessage(ICON_INFO + "Auto mode is active"); - } - if (dataStore.isNsEnableTreatments() && !dataStore.isNsEnableHistorySync()) { - userLogMessage(ICON_HELP + "Past historical data can be uploaded to Nightscout via the History Sync option in Advanced Nightscout Settings."); - } -*/ if (dataStore.isNightscoutUpload() && dataStore.isNsEnableProfileUpload() && commsSuccess > 0) { if (!pumpHistoryHandler.isProfileUploaded()) { - userLogMessage(ICON_INFO + getString(R.string.info_no_profile_uploaded)); - userLogMessage(ICON_HELP + getString(R.string.help_profile_upload_main_menu)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, R.string.info_no_profile_uploaded); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_profile_upload_main_menu); } else if (dataStore.isNameBasalPatternChanged() && (dataStore.isNsEnableProfileSingle() || dataStore.isNsEnableProfileOffset())) { - userLogMessage(ICON_INFO + getString(R.string.info_basal_pattern_names)); - userLogMessage(ICON_HELP + getString(R.string.help_profile_upload_main_menu)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.INFO, R.string.info_basal_pattern_names); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_profile_upload_main_menu); } } if (pumpBatteryError >= ERROR_PUMPBATTERY_AT) { pumpBatteryError = 0; if (dataStore.getLowBatPollInterval() > POLL_PERIOD_MS) { - userLogMessage(ICON_WARN + String.format(Locale.getDefault(), "%s %s", - getString(R.string.warn_pump_battery_low), - getString(R.string.info_pump_low_battery_mode_change))); - userLogMessage(ICON_SETTING + String.format(Locale.getDefault(), "%s %d %s", - getString(R.string.info_low_battery_interval), - dataStore.getLowBatPollInterval() / 60000, - getString(R.string.time_minutes))); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + String.format("{id;%s}. {id;%s}", + R.string.warn_pump_battery_low, + R.string.info_pump_low_battery_mode_change)); + + UserLogMessage.send(mContext, UserLogMessage.TYPE.OPTION, + String.format("{id;%s} %s {id;%s}", + R.string.info_low_battery_interval, + dataStore.getLowBatPollInterval() / 60000, + R.string.time_minutes)); } else { - userLogMessage(ICON_WARN + getString(R.string.warn_pump_battery_low)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.warn_pump_battery_low); } } @@ -892,34 +862,36 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received pumpClockError++; if (pumpClockError >= ERROR_PUMPCLOCK_AT) { pumpClockError = 0; - userLogMessage(ICON_WARN + String.format(Locale.getDefault(), getString(R.string.warn_pump_time_difference), - (int) (Math.abs(pumpClockDifference) / 60000L), - getString(R.string.time_minutes), - pumpClockDifference > 0 ? getString(R.string.time_ahead) : getString(R.string.time_behind))); - userLogMessage(ICON_HELP + getString(R.string.help_pump_time_difference)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + String.format("{id;%s} %s {id;%s} {id;%s}", + R.string.warn_pump_time_difference, + (int) (Math.abs(pumpClockDifference) / 60000L), + R.string.time_minutes, + pumpClockDifference > 0 ? R.string.warn_pump_time_difference_ahead : R.string.warn_pump_time_difference_behind)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_pump_time_difference); } if (commsError >= ERROR_COMMS_AT) { - userLogMessage(ICON_WARN + getString(R.string.warn_multiple_comms_errors)); - userLogMessage(ICON_HELP + getString(R.string.help_multiple_comms_errors)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.warn_multiple_comms_errors); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_multiple_comms_errors); } if (pumpLostSensorError >= ERROR_PUMPLOSTSENSOR_AT) { pumpLostSensorError = 0; - userLogMessage(ICON_WARN + getString(R.string.warn_missing_transmissions)); - userLogMessage(ICON_HELP + getString(R.string.help_missing_transmissions)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.warn_missing_transmissions); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_missing_transmissions); } if (commsConnectError >= ERROR_CONNECT_AT * (dataStore.isDoublePollOnPumpAway() ? 2 : 1)) { commsConnectError = 0; - userLogMessage(ICON_WARN + getString(R.string.warn_connection_fail)); - userLogMessage(ICON_HELP + getString(R.string.help_connection_fail)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.warn_connection_fail); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_connection_fail); } if (commsSignalError >= ERROR_SIGNAL_AT) { commsSignalError = 0; - userLogMessage(ICON_WARN + getString(R.string.warn_rssi_signal)); - userLogMessage(ICON_HELP + getString(R.string.help_rssi_signal)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, R.string.warn_rssi_signal); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP, R.string.help_rssi_signal); } } @@ -963,22 +935,26 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received private boolean isHistoryNeeded() { boolean historyNeeded = false; String logTAG = "historyNeeded: "; - String userlogTAG = ICON_REFRESH + getString(R.string.history_text) + ": "; int recency = Math.round((float) pumpHistoryHandler.pumpHistoryRecency() / 60000L); if (recency == -1 || recency > 24 * 60) { Log.d(TAG, logTAG + "no recent data"); - userLogMessage(userlogTAG + getString(R.string.history_no_recent_data)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: no recent data", R.string.history_text)); historyNeeded = true; statPoll.incHistoryReqRecency(); } else if (recency >= 3 * 60) { Log.d(TAG, logTAG + "recency " + (recency < 120 ? recency + " minutes" : ">" + recency / 60 + " hours")); - userLogMessage(userlogTAG + getString(R.string.history_recency) + " " + (recency < 120 ? recency + " " + getString(R.string.time_minutes) : ">" + recency / 60 + " " + getString(R.string.time_hours))); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s} %s {id;%s}", R.string.history_text, R.string.history_recency, + recency < 120 ? recency : ">" + recency / 60, + recency < 120 ? R.string.time_minutes : R.string.time_hours)); historyNeeded = true; statPoll.incHistoryReqRecency(); } else if (dataStore.getSysPumpHistoryFrequency() > 0 && (recency >= dataStore.getSysPumpHistoryFrequency() && pumpHistoryHandler.isLoopActivePotential())) { Log.d(TAG, logTAG + "auto mode"); - userLogMessage(userlogTAG + getString(R.string.history_auto_mode)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_auto_mode)); historyNeeded = true; statPoll.incHistoryReqAutoMode(); } @@ -1057,7 +1033,10 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (ageMinutes > 15) { Log.d(TAG, logTAG + "stale status " + (ageMinutes < 120 ? ageMinutes + " minutes" : ">" + ageMinutes / 60 + " hours")); - userLogMessage(userlogTAG + getString(R.string.history_stale_status) + " " + (ageMinutes < 120 ? ageMinutes + " " + getString(R.string.time_minutes) : ">" + ageMinutes / 60 + " " + getString(R.string.time_hours))); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s} %s {id;%s}", R.string.history_text, R.string.history_stale_status, + ageMinutes < 120 ? ageMinutes : ">" + ageMinutes / 60, + ageMinutes < 120 ? R.string.time_minutes : R.string.time_hours)); historyNeeded = true; statPoll.incHistoryReqStale(); @@ -1066,7 +1045,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // basal pattern changed? if (basalPatNow != 0 && basalPatPre != 0 && basalPatNow != basalPatPre) { Log.d(TAG, logTAG + "basal pattern changed"); - userLogMessage(userlogTAG + getString(R.string.history_basal_pattern_changed)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_basal_pattern_changed)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1074,12 +1054,14 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // suspend/resume? if (results.first().isSuspended() && !results.get(1).isSuspended()) { Log.d(TAG, logTAG + "basal suspend"); - userLogMessage(userlogTAG + getString(R.string.history_basal_suspend)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_basal_suspend)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } else if (!results.first().isSuspended() && results.get(1).isSuspended()) { Log.d(TAG, logTAG + "basal resume"); - userLogMessage(userlogTAG + getString(R.string.history_basal_resume)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_basal_resume)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1087,7 +1069,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // new temp basal? if (results.first().isTempBasalActive() && !results.get(1).isTempBasalActive()) { Log.d(TAG, logTAG + "temp basal"); - userLogMessage(userlogTAG + getString(R.string.history_temp_basal)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_temp_basal)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1096,7 +1079,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (!results.first().isTempBasalActive() && results.get(1).isTempBasalActive() && Math.abs(tempMinsPre - ageMinutes) > 4) { Log.d(TAG, logTAG + "temp ended"); - userLogMessage(userlogTAG + getString(R.string.history_temp_ended)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_temp_ended)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1105,7 +1089,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (results.first().isTempBasalActive() && results.get(1).isTempBasalActive() && Math.abs(tempMinsDiff - ageMinutes) > 4) { Log.d(TAG, logTAG + "temp extended"); - userLogMessage(userlogTAG + getString(R.string.history_temp_extended)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_temp_extended)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1117,7 +1102,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (!results.first().isBolusingDual() && results.get(1).isBolusingDual()) { if (Math.abs(bolusMinsPre - ageMinutes) > 4) { Log.d(TAG, logTAG + "dual ended"); - userLogMessage(userlogTAG + getString(R.string.history_dual_ended)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_dual_ended)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1126,7 +1112,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received else if (!results.first().isBolusingSquare() && results.get(1).isBolusingSquare() && !results.get(1).isBolusingNormal()) { if (Math.abs(bolusMinsPre - ageMinutes) > 4) { Log.d(TAG, logTAG + "square ended"); - userLogMessage(userlogTAG + getString(R.string.history_square_ended)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_square_ended)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1134,14 +1121,16 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // dual bolus normal part delivered? else if (!results.first().isBolusingSquare() && !results.get(1).isBolusingDual() && results.first().isBolusingDual()) { Log.d(TAG, logTAG + "dual bolus"); - userLogMessage(userlogTAG + getString(R.string.history_dual_bolus)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_dual_bolus)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } // normal bolus delivered else { Log.d(TAG, logTAG + "normal bolus"); - userLogMessage(userlogTAG + getString(R.string.history_normal_bolus)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_normal_bolus)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1150,7 +1139,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received else if (!results.first().isBolusingDual() && results.get(1).isBolusingDual() && Math.abs(bolusMinsPre - ageMinutes) > 4) { Log.d(TAG, logTAG + "bolus ended"); - userLogMessage(userlogTAG + getString(R.string.history_bolus_ended)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_bolus_ended)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1158,7 +1148,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // square bolus started? if (!results.first().isBolusingNormal() && results.first().isBolusingSquare() && !results.get(1).isBolusingSquare()) { Log.d(TAG, logTAG + "square bolus"); - userLogMessage(userlogTAG + getString(R.string.history_square_bolus)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_square_bolus)); historyNeeded = true; statPoll.incHistoryReqTreatment(); } @@ -1169,21 +1160,24 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (results.first().getReservoirAmount() > results.get(1).getReservoirAmount()) { Log.d(TAG, logTAG + "reservoir changed"); - userLogMessage(userlogTAG + getString(R.string.history_reservoir_changed)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_reservoir_changed)); historyNeeded = true; statPoll.incHistoryReqConsumable(); } if (results.first().getBatteryPercentage() > results.get(1).getBatteryPercentage()) { Log.d(TAG, logTAG + "battery changed"); - userLogMessage(userlogTAG + getString(R.string.history_battery_changed)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_battery_changed)); historyNeeded = true; statPoll.incHistoryReqConsumable(); } if (!historyNeeded && results.get(1).getActiveBasalPattern() == 0) { Log.d(TAG, logTAG + "pattern resume"); - userLogMessage(userlogTAG + getString(R.string.history_pattern_resume)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_pattern_resume)); historyNeeded = true; statPoll.incHistoryReqConsumable(); } @@ -1197,7 +1191,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received && bolusRefNow == bolusRefPre && basalChange >= 0 && sumChange < -0.1) { Log.d(TAG, logTAG + "cannula fill"); - userLogMessage(userlogTAG + getString(R.string.history_cannula_fill)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_cannula_fill)); historyNeeded = true; statPoll.incHistoryReqConsumable(); } @@ -1224,7 +1219,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (results.first().isCgmWarmUp() && (cgmresults.size() > 1 && !cgmresults.get(1).isCgmWarmUp())) { Log.d(TAG, logTAG + "sensor changed"); - userLogMessage(userlogTAG + getString(R.string.history_sensor_changed)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_sensor_changed)); historyNeeded = true; statPoll.incHistoryReqConsumable(); } @@ -1239,7 +1235,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received .findAll() .size() == 1) { Log.d(TAG, logTAG + "recent finger bg"); - userLogMessage(userlogTAG + getString(R.string.history_recent_finger_bg)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_recent_finger_bg)); historyNeeded = true; statPoll.incHistoryReqBG(); } @@ -1251,7 +1248,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received && results.first().getCalibrationDueMinutes() > 0 && results.first().getCalibrationDueMinutes() > cgmresults.get(1).getCalibrationDueMinutes()) { Log.d(TAG, logTAG + "calibration info"); - userLogMessage(userlogTAG + getString(R.string.history_calibration_info)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_calibration_info)); historyNeeded = true; statPoll.incHistoryReqCalibration(); } @@ -1264,7 +1262,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received long dayPre = results.get(1).getEventDate().getTime() + pumpClockDifference; if (!sdfDay.format(dayNow).equals(sdfDay.format(dayPre))) { Log.d(TAG, logTAG + "daily totals"); - userLogMessage(userlogTAG + getString(R.string.history_daily_totals)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_daily_totals)); historyNeeded = true; } } @@ -1273,13 +1272,15 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (alarms && results.first().getAlert() != 0) { if (results.first().getAlertRTC() != results.get(1).getAlertRTC()) { Log.d(TAG, logTAG + "active alert"); - userLogMessage(userlogTAG + getString(R.string.history_active_alert)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_active_alert)); historyNeeded = true; statPoll.incHistoryReqAlert(); } else if (recency >= 15) { // recheck at interval to catch stacked alerts as pump will only show the oldest uncleared active alert Log.d(TAG, logTAG + "alert recheck"); - userLogMessage(userlogTAG + getString(R.string.history_alert_recheck)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HISTORY, + String.format("{id;%s}: {id;%s}", R.string.history_text, R.string.history_alert_recheck)); historyNeeded = true; statPoll.incHistoryReqAlertRecheck(); } @@ -1408,20 +1409,20 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received private boolean openUsbDevice() { if (!hasUsbHostFeature()) { Log.e(TAG, "Device does not support USB OTG"); - userLogMessage(ICON_WARN + getString(R.string.error_usb_no_support)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, getString(R.string.error_usb_no_support)); return false; } if (mUsbManager == null) { Log.e(TAG, "USB connection error. mUsbManager == null"); - userLogMessage(ICON_WARN + getString(R.string.error_usb_no_connection)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, getString(R.string.error_usb_no_connection)); return false; } UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID); if (cnlStick == null) { Log.w(TAG, "USB connection error. Is the CNL plugged in?"); - userLogMessage(ICON_WARN + getString(R.string.error_usb_no_connection)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, getString(R.string.error_usb_no_connection)); return false; } @@ -1435,7 +1436,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received mHidDevice.open(); } catch (Exception e) { Log.e(TAG, "Unable to open serial device", e); - userLogMessage(ICON_WARN + getString(R.string.error_usb_no_open)); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, getString(R.string.error_usb_no_open)); return false; } @@ -1494,17 +1495,19 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received private boolean debugHistory(boolean debug) { if (debug) { if (pumpHistoryHandler.records() == 0) { - userLogMessage("Getting history from file"); + UserLogMessage.send(mContext, "Getting history from file"); new HistoryDebug(mContext, pumpHistoryHandler).run(); - userLogMessage("Processing done"); + UserLogMessage.send(mContext, "Processing done"); } else { - userLogMessage("History file already processed"); + UserLogMessage.send(mContext, "History file already processed"); } } return debug; } private void debugStatusMessage() { + DateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.US); + long now = System.currentTimeMillis(); Date lastDate = pumpHistoryHandler.debugNoteLastDate(); @@ -1522,7 +1525,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received byte[] payload; for (PumpStatusEvent record : results) { - note += " [" + dateFormatter.format(record.getEventDate()) + "] "; + note += " [" + sdf.format(record.getEventDate()) + "] "; byte status = record.getPumpStatus(); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryAlarm.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryAlarm.java index 1d21b30adc41b3ea1e1bdb4f22f3b511f5257cc6..351b7a407b52c251bef090f3096b09641b4f0f82 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryAlarm.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryAlarm.java @@ -11,7 +11,7 @@ import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.ToolKit; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.RealmResults; @@ -273,7 +273,7 @@ public class PumpHistoryAlarm extends RealmObject implements PumpHistoryInterfac .type(type) .date(alarmedDate) .priority(priority) - .clock(FormatKit.getInstance().formatAsClock(alarmedDate)) + .clock(FormatKit.getInstance().formatAsClock(alarmedDate.getTime()).replace(" ", "")) .title(title) .message(message) .extended(extended) diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBG.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBG.java index f30ee4646d5c0ee1fd97ad447a634a9a7baa22c2..36e9bdebe78d0e5b730573e931ef5f67fdba8fdd 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBG.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBG.java @@ -13,8 +13,8 @@ import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.EntriesEndpoints; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.EntriesEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmList; import io.realm.RealmObject; @@ -162,7 +162,7 @@ public class PumpHistoryBG extends RealmObject implements PumpHistoryInterface { .key(key) .type(type) .date(date) - .clock(FormatKit.getInstance().formatAsClock(date)) + .clock(FormatKit.getInstance().formatAsClock(date.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBasal.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBasal.java index 7b243457d3f28611d0ddf3192486af24d4e6b954..81d13b21b2d34bdc12ba5af4269bde6373b779d1 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBasal.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBasal.java @@ -11,7 +11,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import info.nightscout.android.history.NightscoutItem; import io.realm.Realm; import io.realm.RealmObject; @@ -249,7 +249,7 @@ public class PumpHistoryBasal extends RealmObject implements PumpHistoryInterfac .key(key) .type(type) .date(eventDate) - .clock(FormatKit.getInstance().formatAsClock(eventDate)) + .clock(FormatKit.getInstance().formatAsClock(eventDate.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java index 10fbdba334b75ce089f2d339bf64a7c844bcaa37..e2f511b70cadd7d211804ace42aa89a4c4dfd9c1 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java @@ -11,7 +11,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import info.nightscout.android.history.NightscoutItem; import io.realm.Realm; import io.realm.RealmObject; @@ -383,7 +383,7 @@ public class PumpHistoryBolus extends RealmObject implements PumpHistoryInterfac .key(key) .type(MessageItem.TYPE.BOLUS) .date(date) - .clock(FormatKit.getInstance().formatAsClock(date)) + .clock(FormatKit.getInstance().formatAsClock(date.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java index 42f535b0173478f1e85a4e53adce0fff31033260..fdc780044a40e146b43d02ead57c33eed156d4a5 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java @@ -8,7 +8,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; -import info.nightscout.api.EntriesEndpoints; +import info.nightscout.android.upload.nightscout.EntriesEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.annotations.Ignore; diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryDaily.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryDaily.java index 3d3cd562b3e36c10b2d114c6b71319314b9a1344..1ea5aa41f4732521322c5d4c59df365cf3aabafe 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryDaily.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryDaily.java @@ -14,7 +14,7 @@ import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.annotations.Ignore; diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryLoop.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryLoop.java index 04c1cd83810eeece61f8cab7be452643b6a84135..d062a2581752912473ed86d33ef426ad6a88de5c 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryLoop.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryLoop.java @@ -10,7 +10,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.annotations.Ignore; diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMarker.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMarker.java index fe62fbb0f4ffc1b33ff6551f79e28302eb6e9eaf..d286cb509402b391ed16a445f555825f6b2f9090 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMarker.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMarker.java @@ -12,7 +12,7 @@ import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.annotations.Ignore; @@ -204,7 +204,7 @@ public class PumpHistoryMarker extends RealmObject implements PumpHistoryInterfa messageItems.add(new MessageItem() .key(key) .date(eventDate) - .clock(FormatKit.getInstance().formatAsClock(eventDate)) + .clock(FormatKit.getInstance().formatAsClock(eventDate.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMisc.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMisc.java index cd344a8394ba7a84c5c86d15d1fc8b628f52ca26..97c4debe5f8898beac81ec5fe751281764b6d861 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMisc.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryMisc.java @@ -14,7 +14,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.PumpHistoryParser; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import info.nightscout.android.history.NightscoutItem; import io.realm.Realm; @@ -196,7 +196,7 @@ public class PumpHistoryMisc extends RealmObject implements PumpHistoryInterface .key(key) .type(MessageItem.TYPE.CONSUMABLE) .date(eventDate) - .clock(FormatKit.getInstance().formatAsClock(eventDate)) + .clock(FormatKit.getInstance().formatAsClock(eventDate.getTime()).replace(" ", "")) .title(title) .message(message)); @@ -204,15 +204,18 @@ public class PumpHistoryMisc extends RealmObject implements PumpHistoryInterface } private String formatLifetimes() { - String text = String.format("%s ", - FormatKit.getInstance().getString(R.string.Lifetimes)); + StringBuilder sb = new StringBuilder( + String.format("%s ", + FormatKit.getInstance().getString(R.string.Lifetimes))); + for (int lt = 0; lt < LIFETIMES_TOTAL; lt++) { byte days = lifetimes[lt << 1]; byte hours = lifetimes[(lt << 1) + 1]; - text += (lt == 0 ? "" : " ") + ((days | hours) == 0 ? "---" : - FormatKit.getInstance().formatHoursAsDH((days * 24) + hours)); + if (lt > 0) sb.append(" "); + sb.append((days | hours) == 0 ? "---" : FormatKit.getInstance().formatHoursAsDH((days * 24) + hours)); } - return text; + + return sb.toString(); } private String formatCalibrations() { diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryPattern.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryPattern.java index 172b70f6952f51296bf8ad2a0caca051fc7f5406..2970ad4858126ef186fec354210008c584bae7f4 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryPattern.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryPattern.java @@ -10,7 +10,7 @@ import info.nightscout.android.R; import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import info.nightscout.android.history.NightscoutItem; import io.realm.Realm; import io.realm.RealmObject; @@ -78,7 +78,7 @@ public class PumpHistoryPattern extends RealmObject implements PumpHistoryInterf .key(key) .type(MessageItem.TYPE.BASAL) .date(eventDate) - .clock(FormatKit.getInstance().formatAsClock(eventDate)) + .clock(FormatKit.getInstance().formatAsClock(eventDate.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java index 6992b8613b06df53578e86c4ea226fe05cd01773..aa8028f0d3e2114e08d9ff1c583d61959bcd646e 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java @@ -16,8 +16,8 @@ import java.util.TimeZone; import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistorySender; -import info.nightscout.api.ProfileEndpoints; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.ProfileEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmObject; import io.realm.annotations.Ignore; diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistorySystem.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistorySystem.java index 36a4319987bd333a4d27c167a3203eee5c9ab6bb..9e976be79605747caf544657eabcfd6a075a4a94 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistorySystem.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistorySystem.java @@ -10,7 +10,7 @@ import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.utils.FormatKit; -import info.nightscout.api.TreatmentsEndpoints; +import info.nightscout.android.upload.nightscout.TreatmentsEndpoints; import io.realm.Realm; import io.realm.RealmList; import io.realm.RealmObject; @@ -174,7 +174,7 @@ public class PumpHistorySystem extends RealmObject implements PumpHistoryInterfa .date(updateDate) .type(type) .priority(priority) - .clock(FormatKit.getInstance().formatAsClock(updateDate)) + .clock(FormatKit.getInstance().formatAsClock(updateDate.getTime()).replace(" ", "")) .title(title) .message(message)); diff --git a/app/src/main/java/info/nightscout/android/model/store/DataStore.java b/app/src/main/java/info/nightscout/android/model/store/DataStore.java index d93ae718633b0f651702a2819a064e6adb48e8ce..c62c2fb2b56c7672bdaae778c29ebd0c63f5cf01 100644 --- a/app/src/main/java/info/nightscout/android/model/store/DataStore.java +++ b/app/src/main/java/info/nightscout/android/model/store/DataStore.java @@ -54,6 +54,9 @@ public class DataStore extends RealmObject { private long lowBatPollInterval; private boolean doublePollOnPumpAway; + private boolean enableXdripPlusUpload; + private boolean xdripPlusUploadAvailable; + private boolean sysEnableCgmHistory; private int sysCgmHistoryDays; private boolean sysEnablePumpHistory; @@ -255,6 +258,8 @@ public class DataStore extends RealmObject { nightscoutURL = sharedPreferences.getString(context.getString(R.string.preference_nightscout_url), ""); nightscoutSECRET = sharedPreferences.getString(context.getString(R.string.preference_api_secret), "YOURAPISECRET"); + enableXdripPlusUpload = sharedPreferences.getBoolean(context.getString(R.string.preference_enable_xdrip_plus), false); + // system sysEnableCgmHistory = sharedPreferences.getBoolean("sysEnableCgmHistory", true); sysCgmHistoryDays = Integer.parseInt(sharedPreferences.getString("sysCgmHistoryDays", "7")); @@ -650,6 +655,18 @@ public class DataStore extends RealmObject { this.pumpBatteryError = 0; } + public boolean isEnableXdripPlusUpload() { + return enableXdripPlusUpload; + } + + public boolean isXdripPlusUploadAvailable() { + return xdripPlusUploadAvailable; + } + + public void setXdripPlusUploadAvailable(boolean xdripPlusUploadAvailable) { + this.xdripPlusUploadAvailable = xdripPlusUploadAvailable; + } + public boolean isMmolxl() { return mmolxl; } diff --git a/app/src/main/java/info/nightscout/android/model/store/UserLog.java b/app/src/main/java/info/nightscout/android/model/store/UserLog.java index 048ec3448d5ae18fde687d9dc4b3f57e97543fd8..8258243afe8c12da2d84150f226f8ce4f422b10e 100644 --- a/app/src/main/java/info/nightscout/android/model/store/UserLog.java +++ b/app/src/main/java/info/nightscout/android/model/store/UserLog.java @@ -1,27 +1,106 @@ package info.nightscout.android.model.store; +import java.util.UUID; + +import info.nightscout.android.utils.FormatKit; import io.realm.RealmObject; import io.realm.annotations.Index; +import io.realm.annotations.PrimaryKey; public class UserLog extends RealmObject { + @PrimaryKey + private String key = UUID.randomUUID().toString(); + @Index + private long timestamp; @Index - public long timestamp; - public String message; + private int type; + @Index + private int flag; - public void UserLogMessage(String message) { - UserLogMessage(System.currentTimeMillis(), message); - } + private String message; - public void UserLogMessage(long timestamp, String message) { + public UserLog message(long timestamp, int type, int flag, String message) { this.timestamp = timestamp; + this.type = type; + this.flag = flag; this.message = message; + return this; } public long getTimestamp() { return timestamp; } + public int getType() { + return type; + } + + public int getFlag() { + return flag; + } + public String getMessage() { return message; } + + public String getMessageParsed() { + + StringBuilder sb = new StringBuilder(); + + try { + String parts[] = message.split("[{}]"); + for (String part : parts) { + String data[] = part.split(";"); + switch (data[0]) { + case "id": + sb.append(FormatKit.getInstance().getString(Integer.parseInt(data[1]))); + break; + case "sgv": + sb.append(FormatKit.getInstance().formatAsGlucose(Integer.parseInt(data[1]), false, true)); + break; + case "diff": + sb.append(FormatKit.getInstance().formatSecondsAsDiff(Integer.parseInt(data[1]))); + break; + case "weekday": + sb.append(FormatKit.getInstance().formatAsWeekday(Long.parseLong(data[1]))); + break; + case "date": + sb.append(FormatKit.getInstance().formatAsYMD(Long.parseLong(data[1]))); + break; + case "date.time": + sb.append(FormatKit.getInstance().formatAsYMD(Long.parseLong(data[1]))); + sb.append(" "); + sb.append(FormatKit.getInstance().formatAsClockSeconds(Long.parseLong(data[1]))); + break; + case "time.sgv": + sb.append(FormatKit.getInstance().formatAsClock(Long.parseLong(data[1]))); + break; + case "time.poll": + sb.append(FormatKit.getInstance().formatAsClock(Long.parseLong(data[1]))); + break; + case "time.hist": + sb.append(FormatKit.getInstance().formatAsDayClock(Long.parseLong(data[1]))); + break; + case "time.sgv.e": + sb.append(FormatKit.getInstance().formatAsClockSecondsNoAmPm(Long.parseLong(data[1]))); + break; + case "time.poll.e": + sb.append(FormatKit.getInstance().formatAsClockSecondsNoAmPm(Long.parseLong(data[1]))); + break; + case "time.hist.e": + sb.append(FormatKit.getInstance().formatAsYMD(Long.parseLong(data[1]))); + sb.append(" "); + sb.append(FormatKit.getInstance().formatAsClockSeconds(Long.parseLong(data[1]))); + break; + default: + if (data[0].length() > 0) sb.append(data[0]); + } + } + } catch (Exception e) { + sb.append("???"); + } + + return sb.toString(); + } + } diff --git a/app/src/main/java/info/nightscout/android/pushover/PushoverUploadService.java b/app/src/main/java/info/nightscout/android/pushover/PushoverUploadService.java index 4157a121774428a7c442dfaa0665ed2c2f7d464b..99561e6d7667f5e6ca5cf3fa937891df792639a9 100644 --- a/app/src/main/java/info/nightscout/android/pushover/PushoverUploadService.java +++ b/app/src/main/java/info/nightscout/android/pushover/PushoverUploadService.java @@ -17,7 +17,7 @@ import info.nightscout.android.UploaderApplication; import info.nightscout.android.history.MessageItem; import info.nightscout.android.history.PumpHistoryHandler; import info.nightscout.android.medtronic.Stats; -import info.nightscout.android.medtronic.service.MasterService; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.model.medtronicNg.PumpHistoryInterface; import info.nightscout.android.model.store.DataStore; import info.nightscout.android.model.store.StatPushover; @@ -25,15 +25,13 @@ import io.realm.Realm; import okhttp3.Headers; import retrofit2.Response; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_INFO; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_WARN; import static info.nightscout.android.utils.ToolKit.getWakeLock; import static info.nightscout.android.utils.ToolKit.releaseWakeLock; public class PushoverUploadService extends Service { private static final String TAG = PushoverUploadService.class.getSimpleName(); - private final String url = "https://api.pushover.net/"; + private static final String PUSHOVER_URL = "https://api.pushover.net/"; private Context mContext; @@ -101,7 +99,7 @@ public class PushoverUploadService extends Service { statPushover = (StatPushover) Stats.getInstance().readRecord(StatPushover.class); statPushover.incRun(); - pushoverApi = new PushoverApi(url); + pushoverApi = new PushoverApi(PUSHOVER_URL); if (isValid()) process(); else statPushover.incValidError(); } @@ -113,16 +111,6 @@ public class PushoverUploadService extends Service { } } - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - mContext.sendBroadcast(intent); - } catch (Exception ignored) { - } - } - public boolean isValid() { valid = dataStore.isPushoverValidated(); @@ -134,7 +122,7 @@ public class PushoverUploadService extends Service { try { if (!valid && apiToken.equals(apiCheck) && userToken.equals(userCheck)) { - userLogMessage(ICON_WARN + "Pushover: validation failed. Check that your Pushover account is active and your account settings are correct."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN,"Pushover validation failed. Check that your Pushover account is active and your account settings are correct."); throw new Exception("account error"); } @@ -162,13 +150,15 @@ public class PushoverUploadService extends Service { String status = response.body().getStatus(); if (response.code() == 400 || status == null || !status.equals("1")) { - userLogMessage(ICON_WARN + "Pushover: validation failed. Check that your Pushover account is active and your account settings are correct."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + "Pushover validation failed. Check that your Pushover account is active and your account settings are correct."); throw new Exception("account error"); } else { valid = true; - userLogMessage(ICON_INFO + "Pushover: validation success"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.SHARE, + "Pushover is available"); String[] devices = response.body().getDevices(); if (devices != null) { @@ -179,7 +169,8 @@ public class PushoverUploadService extends Service { sb.append("'"); } if (sb.length() > 0) - userLogMessage(ICON_INFO + "Pushover devices: " + sb.toString()); + UserLogMessage.sendE(mContext, + "Pushover devices: " + sb.toString()); } } } @@ -245,7 +236,9 @@ public class PushoverUploadService extends Service { DateFormat df = new SimpleDateFormat("yyyy/MM/dd", Locale.ENGLISH); Log.i(TAG, String.format(Locale.ENGLISH, "Sent: %d Limit: %d Remaining: %d Reset: %s", messagesSent, appLimit, appRemaining, df.format(appReset * 1000))); - + UserLogMessage.sendN(mContext, + String.format("Pushover messages sent: %s", + messagesSent)); } } @@ -421,6 +414,15 @@ public class PushoverUploadService extends Service { } } catch (Exception ignored) {} + UserLogMessage.sendE(mContext, + String.format("Pushover: %s/%s {date.time;%s} '%s' '%s'", + appLimit - appRemaining, + appLimit, + messageItem.getDate().getTime(), + title, + message + )); + messagesSent++; success = true; diff --git a/app/src/main/java/info/nightscout/api/DeviceEndpoints.java b/app/src/main/java/info/nightscout/android/upload/nightscout/DeviceEndpoints.java similarity index 97% rename from app/src/main/java/info/nightscout/api/DeviceEndpoints.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/DeviceEndpoints.java index 248c3cc0d9aaa450970f472ba672f79356bf66eb..6f086f19105d25b97afdba9fdfa5fec307adad0e 100644 --- a/app/src/main/java/info/nightscout/api/DeviceEndpoints.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/DeviceEndpoints.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import java.math.BigDecimal; import java.util.Date; diff --git a/app/src/main/java/info/nightscout/api/EntriesEndpoints.java b/app/src/main/java/info/nightscout/android/upload/nightscout/EntriesEndpoints.java similarity index 98% rename from app/src/main/java/info/nightscout/api/EntriesEndpoints.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/EntriesEndpoints.java index d29d0864608954c63117fea76d09a7b6bca77e62..146944753f0c7e3046d3d6a3b5c23102a7d288fe 100644 --- a/app/src/main/java/info/nightscout/api/EntriesEndpoints.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/EntriesEndpoints.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java index 6e84d02c55dd4e546c7463d77f62b5dde80e2651..5113540523e64211e6aeefb23e3a5787f1affb10 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.TimeZone; import info.nightscout.android.history.NightscoutItem; import info.nightscout.android.history.PumpHistoryParser; @@ -19,16 +20,11 @@ import info.nightscout.android.history.PumpHistorySender; import info.nightscout.android.model.medtronicNg.PumpHistoryInterface; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.model.store.DataStore; -import info.nightscout.api.DeviceEndpoints; -import info.nightscout.api.DeviceEndpoints.Iob; -import info.nightscout.api.DeviceEndpoints.Battery; -import info.nightscout.api.DeviceEndpoints.PumpStatus; -import info.nightscout.api.DeviceEndpoints.PumpInfo; -import info.nightscout.api.DeviceEndpoints.DeviceStatus; -import info.nightscout.api.EntriesEndpoints; -import info.nightscout.api.ProfileEndpoints; -import info.nightscout.api.TreatmentsEndpoints; -import info.nightscout.api.UploadApi; +import info.nightscout.android.upload.nightscout.DeviceEndpoints.Iob; +import info.nightscout.android.upload.nightscout.DeviceEndpoints.Battery; +import info.nightscout.android.upload.nightscout.DeviceEndpoints.PumpStatus; +import info.nightscout.android.upload.nightscout.DeviceEndpoints.PumpInfo; +import info.nightscout.android.upload.nightscout.DeviceEndpoints.DeviceStatus; import io.realm.Realm; import okhttp3.ResponseBody; import retrofit2.Response; @@ -91,13 +87,11 @@ public class NightScoutUpload { uploadEvents(records); } + // Format date to Zulu (UTC) time public static String formatDateForNS(Date date) { - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); - SimpleDateFormat dftz = new SimpleDateFormat("Z", Locale.getDefault()); - String tz = dftz.format(date); - if (tz.length() == 5 && (tz.startsWith("+") || tz.startsWith("-"))) - return df.format(date) + tz.subSequence(0,3) + ":" + tz.subSequence(3,5); - return df.format(date); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + return sdf.format(date) + "Z"; } private void uploadEvents(List<PumpHistoryInterface> records) throws Exception { @@ -439,7 +433,7 @@ public class NightScoutUpload { if (dataStore.isNsEnableHistorySync()) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm", Locale.US); long now = System.currentTimeMillis(); diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java deleted file mode 100644 index 2213d1c5de4c36094124f4c49388fb2077f489ef..0000000000000000000000000000000000000000 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java +++ /dev/null @@ -1,32 +0,0 @@ -package info.nightscout.android.upload.nightscout; - -import retrofit2.Call; -import retrofit2.http.GET; -/** - * Created by lgoedhart on 26/06/2016. - */ -public interface NightscoutApi { - class LoginStatus { - public final String status; - - public LoginStatus(String status) { - this.status = status; - } - } - - class SiteStatus { - public final String status; - public final String name; - - public SiteStatus(String status, String name) { - this.status = status; - this.name = name; - } - } - - @GET("/api/v1/status.json") - Call<SiteStatus> getStatus(); - - @GET("/api/v1/experiments/update") - Call<LoginStatus> testLogin(); -} diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java index c482cf18d512d707d7b344a8cfa50a1a9bb76643..e7c49229ee00fcd27355b1272a9e485a3983d5ee 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java @@ -1,7 +1,6 @@ package info.nightscout.android.upload.nightscout; import android.content.Context; -import android.content.Intent; import android.support.annotation.NonNull; import android.util.Log; @@ -11,17 +10,11 @@ import java.security.NoSuchAlgorithmException; import java.util.List; import info.nightscout.android.UploaderApplication; -import info.nightscout.android.medtronic.service.MasterService; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.model.store.DataStore; -import info.nightscout.api.StatusEndpoints; -import info.nightscout.api.UploadApi; import io.realm.Realm; import retrofit2.Response; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_HELP; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_INFO; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_WARN; - /** * Created by Pogman on 21.1.18. */ @@ -81,7 +74,7 @@ public class NightscoutStatus { if (!UploaderApplication.isOnline()) { available = false; if (dataStore.isDbgEnableUploadErrors()) - userLogMessage(ICON_WARN + "Offline / No internet service"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN,"Offline / No internet service"); } else { @@ -100,79 +93,70 @@ public class NightscoutStatus { if (available && dataStore.isDbgEnableUploadErrors()) { if (!dataStore.isNightscoutAvailable()) { - userLogMessage(ICON_INFO + "Nightscout site is available"); - } - - /* This doesn't seem to be a requirement for newer versions of NS - if (!ns_authDefaultRoles.contains("readable")) { - userLogMessage(ICON_WARN + "Nightscout 'authDefaultRoles' check has failed, data can not be uploaded."); - userLogMessage(ICON_HELP + "Add 'AUTH_DEFAULT_ROLES' and set to 'readable devicestatus-upload' in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.SHARE,"Nightscout site is available"); } - */ if (dataStore.isNsEnableTreatments() && !ns_careportal) { - userLogMessage(ICON_WARN + "Careportal is not enabled in Nightscout, treatment data can not be uploaded."); - userLogMessage(ICON_HELP + "Add 'careportal' to your ENABLE string in Azure or Heroku. Treatments can be disabled in the 'Advanced Nightscout Settings' menu."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN,"Careportal is not enabled in Nightscout, treatment data can not be uploaded."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Add 'careportal' to your ENABLE string in Azure or Heroku. Treatments can be disabled in the 'Advanced Nightscout Settings' menu."); } if (report) { reporttime = now; - if (dataStore.isDbgEnableExtendedErrors()) { - userLogMessage("NS version: " + ns_version); - } + UserLogMessage.sendE(mContext, "NS version: " + ns_version); if (ns_version_major <= NS_MAJOR && (ns_version_minor < NS_MINOR || (ns_version_minor == NS_MINOR && ns_version_point < NS_POINT))) { - userLogMessage(ICON_HELP + "Your version of Nightscout is out of date. It is recommended to use the latest release version of Nightscout with the 600 Series Uploader."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Your version of Nightscout is out of date. It is recommended to use the latest release version of Nightscout with the 600 Series Uploader."); } if (!(ns_enable.contains("PUMP") || ns_enable.contains("pump"))) { - userLogMessage(ICON_HELP + "Pump status information can be shown in Nightscout. Add 'pump' to your ENABLE string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Pump status information can be shown in Nightscout. Add 'pump' to your ENABLE string in Azure or Heroku."); } else { if (!ns_devicestatus) { - userLogMessage(ICON_HELP + "Pump status details can be shown in Nightscout. Add 'DEVICESTATUS_ADVANCED' with value 'true' to your strings in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Pump status details can be shown in Nightscout. Add 'DEVICESTATUS_ADVANCED' with value 'true' to your strings in Azure or Heroku."); } if (ns_pumpFields.equals("")) { - userLogMessage(ICON_HELP + "Pump battery, insulin, delivery modes, calibration and active error alert can be shown in Nightscout. Add 'PUMP_FIELDS' with value 'clock reservoir battery status' to your strings in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Pump battery, insulin, delivery modes, calibration and active error alert can be shown in Nightscout. Add 'PUMP_FIELDS' with value 'clock reservoir battery status' to your strings in Azure or Heroku."); } else { if (!(ns_pumpFields.contains("STATUS") || ns_pumpFields.contains("status"))) - userLogMessage(ICON_HELP + "Bolus delivery modes, error alerts and calibration time can be shown in Nightscout. Add 'status' to your 'PUMP_FIELDS' string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Bolus delivery modes, error alerts and calibration time can be shown in Nightscout. Add 'status' to your 'PUMP_FIELDS' string in Azure or Heroku."); if (!(ns_pumpFields.contains("RESERVOIR") || ns_pumpFields.contains("reservoir"))) - userLogMessage(ICON_HELP + "Units of insulin remaining in the pump can be shown in Nightscout. Add 'reservoir' to your 'PUMP_FIELDS' string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Units of insulin remaining in the pump can be shown in Nightscout. Add 'reservoir' to your 'PUMP_FIELDS' string in Azure or Heroku."); if (!(ns_pumpFields.contains("BATTERY") || ns_pumpFields.contains("battery"))) - userLogMessage(ICON_HELP + "Pump battery percentage remaining can be shown in Nightscout. Add 'battery' to your 'PUMP_FIELDS' string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Pump battery percentage remaining can be shown in Nightscout. Add 'battery' to your 'PUMP_FIELDS' string in Azure or Heroku."); if (!(ns_pumpFields.contains("CLOCK") || ns_pumpFields.contains("clock"))) - userLogMessage(ICON_HELP + "Time since last pump reading can shown on Nightscout. Add 'clock' to your 'PUMP_FIELDS' string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Time since last pump reading can shown on Nightscout. Add 'clock' to your 'PUMP_FIELDS' string in Azure or Heroku."); } } if (dataStore.isNsEnableTreatments() && dataStore.isNsEnableSensorChange() && !(ns_enable.contains("SAGE") || ns_enable.contains("sage"))) { - userLogMessage(ICON_HELP + "Sensor changes are detected and sent to Nightscout. Add 'sage' to your ENABLE string in Azure or Heroku to see time of last change. This can be disabled in the 'Advanced Nightscout Settings' menu."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Sensor changes are detected and sent to Nightscout. Add 'sage' to your ENABLE string in Azure or Heroku to see time of last change. This can be disabled in the 'Advanced Nightscout Settings' menu."); } if (dataStore.isNsEnableTreatments() && dataStore.isNsEnableReservoirChange() && !(ns_enable.contains("CAGE") || ns_enable.contains("cage"))) { - userLogMessage(ICON_HELP + "Reservoir changes are detected and sent to Nightscout. Add 'cage' to your ENABLE string in Azure or Heroku to see time of last change. This can be disabled in the 'Advanced Nightscout Settings' menu."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Reservoir changes are detected and sent to Nightscout. Add 'cage' to your ENABLE string in Azure or Heroku to see time of last change. This can be disabled in the 'Advanced Nightscout Settings' menu."); } if (dataStore.isNsEnableProfileUpload() && !(ns_enable.contains("PROFILE") || ns_enable.contains("profile"))) { - userLogMessage(ICON_HELP + "Basal Profiles are sent to Nightscout. Add 'profile' to your ENABLE string in Azure or Heroku. This can be disabled in the 'Advanced Nightscout Settings' menu."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Basal Profiles are sent to Nightscout. Add 'profile' to your ENABLE string in Azure or Heroku. This can be disabled in the 'Advanced Nightscout Settings' menu."); } if (!(ns_enable.contains("BASAL") || ns_enable.contains("basal"))) { - userLogMessage(ICON_HELP + "Basal rates profiles and pattern changes can be shown in Nightscout. Add 'basal' to your ENABLE string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"Basal rates profiles and pattern changes can be shown in Nightscout. Add 'basal' to your ENABLE string in Azure or Heroku."); } if (!(ns_enable.contains("IOB") || ns_enable.contains("iob"))) { - userLogMessage(ICON_HELP + "IOB active insulin can be shown in Nightscout. Add 'iob' to your ENABLE string in Azure or Heroku."); + UserLogMessage.send(mContext, UserLogMessage.TYPE.HELP,"IOB active insulin can be shown in Nightscout. Add 'iob' to your ENABLE string in Azure or Heroku."); } } } else if (!available && dataStore.isDbgEnableUploadErrors()) - userLogMessage(ICON_WARN + "Nightscout site is not available"); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN,"Nightscout site is not available"); } } @@ -269,7 +253,7 @@ public class NightscoutStatus { } catch (Exception e) { Log.e(TAG, "Nightscout status check error", e); if (dataStore.isDbgEnableUploadErrors()) - userLogMessage(ICON_WARN + "Nightscout status is not available: " + e.getMessage()); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN,"Nightscout status is not available: " + e.getMessage()); } return available; @@ -284,26 +268,9 @@ public class NightscoutStatus { bytes = digest.digest(); StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b : bytes) { - sb.append(String.format("%02x", b & 0xff)); + sb.append(String.format("%02x", b & 0xFF)); } return sb.toString(); } -/* - private boolean isOnline() { - ConnectivityManager cm = (ConnectivityManager) mContext.getApplicationContext() - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo netInfo = cm.getActiveNetworkInfo(); - return netInfo != null && netInfo.isConnectedOrConnecting(); - } -*/ - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - mContext.sendBroadcast(intent); - } catch (Exception ignored) { - } - } } diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java index 376e24b1d2187e23e514ba6be884ee44bed13ef8..bea59a4f69808d47c0ada396680b1648c8af8db5 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java @@ -13,6 +13,7 @@ import java.util.List; import info.nightscout.android.UploaderApplication; import info.nightscout.android.history.PumpHistoryHandler; import info.nightscout.android.medtronic.Stats; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.medtronic.service.MasterService; import info.nightscout.android.model.medtronicNg.PumpHistoryInterface; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; @@ -22,7 +23,6 @@ import io.realm.Realm; import io.realm.RealmResults; import io.realm.Sort; -import static info.nightscout.android.medtronic.UserLogMessage.Icons.ICON_WARN; import static info.nightscout.android.utils.ToolKit.getWakeLock; import static info.nightscout.android.utils.ToolKit.releaseWakeLock; @@ -172,8 +172,7 @@ public class NightscoutUploadService extends Service { statNightscout.timer(timer); statNightscout.settotalRecords(statNightscout.getTotalRecords() + total); - if (dataStore.isDbgEnableExtendedErrors()) - userLogMessage("Uploaded " + total + " records [" + timer + "ms]"); + UserLogMessage.sendE(mContext, String.format("Uploaded %s records [%sms]", total, timer)); Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", total, timer)); @@ -188,7 +187,8 @@ public class NightscoutUploadService extends Service { }); if (dataStore.isDbgEnableUploadErrors()) - userLogMessage(ICON_WARN + "Uploading to nightscout was unsuccessful: " + e.getMessage()); + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + "Uploading to nightscout was unsuccessful: " + e.getMessage()); } } else { @@ -196,13 +196,4 @@ public class NightscoutUploadService extends Service { } } - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - sendBroadcast(intent); - } catch (Exception ignored) { - } - } } diff --git a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java b/app/src/main/java/info/nightscout/android/upload/nightscout/ProfileEndpoints.java similarity index 98% rename from app/src/main/java/info/nightscout/api/ProfileEndpoints.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/ProfileEndpoints.java index 4f82c6087fc7fdda7d756899c8d73846fc6c9e1a..fb9f93c94d347508fa796c3924ce44cd192a4f3b 100644 --- a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/ProfileEndpoints.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; @@ -7,7 +7,6 @@ import java.util.Date; import java.util.List; import java.util.Map; -import info.nightscout.android.upload.nightscout.NightScoutUpload; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Body; diff --git a/app/src/main/java/info/nightscout/api/StatusEndpoints.java b/app/src/main/java/info/nightscout/android/upload/nightscout/StatusEndpoints.java similarity index 99% rename from app/src/main/java/info/nightscout/api/StatusEndpoints.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/StatusEndpoints.java index 66638bfb1b99543871b78ec13f0cbe3bf9f360c7..b08ac30989b8037c59cfded0ae41bb487182e626 100644 --- a/app/src/main/java/info/nightscout/api/StatusEndpoints.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/StatusEndpoints.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java b/app/src/main/java/info/nightscout/android/upload/nightscout/TreatmentsEndpoints.java similarity index 99% rename from app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/TreatmentsEndpoints.java index 995c1adc41762b81a3a9c3a9986ff06ffc0fd910..70e819e7af9ad56383f6c11fae2943a6d62645dd 100644 --- a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/TreatmentsEndpoints.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/info/nightscout/api/UploadApi.java b/app/src/main/java/info/nightscout/android/upload/nightscout/UploadApi.java similarity index 98% rename from app/src/main/java/info/nightscout/api/UploadApi.java rename to app/src/main/java/info/nightscout/android/upload/nightscout/UploadApi.java index 2204b1df8da0c5c91f92d79e97f004b3bcdbc9df..87456680d0cca12ce84060daa0a1045d3fe48bbf 100644 --- a/app/src/main/java/info/nightscout/api/UploadApi.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/UploadApi.java @@ -1,4 +1,4 @@ -package info.nightscout.api; +package info.nightscout.android.upload.nightscout; import android.support.annotation.NonNull; diff --git a/app/src/main/java/info/nightscout/urchin/Urchin.java b/app/src/main/java/info/nightscout/android/urchin/Urchin.java similarity index 98% rename from app/src/main/java/info/nightscout/urchin/Urchin.java rename to app/src/main/java/info/nightscout/android/urchin/Urchin.java index 9b2791780a26551e768c1a3d206b4cd28a43e83c..57b0bb04dfd0a602ace1430fc6103f3fbf519661 100644 --- a/app/src/main/java/info/nightscout/urchin/Urchin.java +++ b/app/src/main/java/info/nightscout/android/urchin/Urchin.java @@ -1,7 +1,6 @@ -package info.nightscout.urchin; +package info.nightscout.android.urchin; import android.content.Context; -import android.content.Intent; import android.util.Log; import com.getpebble.android.kit.PebbleKit; @@ -20,6 +19,7 @@ import java.util.UUID; import info.nightscout.android.UploaderApplication; import info.nightscout.android.history.PumpHistoryParser; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.medtronic.service.MasterService; import info.nightscout.android.model.medtronicNg.PumpHistoryBasal; import info.nightscout.android.model.medtronicNg.PumpHistoryBolus; @@ -57,6 +57,8 @@ public class Urchin { private DataStore dataStore; private PumpStatusEvent pumpStatusEvent; + private int receivedCount = 0; + private long ignoreACK = 0; private int updateID = 0; @@ -162,6 +164,15 @@ public class Urchin { messageReceiver(); } + public void recieved() { + receivedCount++; + + if (receivedCount == 1) { + UserLogMessage.send(mContext, UserLogMessage.TYPE.SHARE, + "Urchin is active"); + } + } + public void update() { storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration()); dataStore = storeRealm.where(DataStore.class).findFirst(); @@ -284,7 +295,8 @@ public class Urchin { if (transactionId != updateID && System.currentTimeMillis() > ignoreACK) refresh(); - + else + recieved(); } }; @@ -1007,13 +1019,4 @@ public class Urchin { return text; } - protected void userLogMessage(String message) { - try { - Intent intent = - new Intent(MasterService.Constants.ACTION_USERLOG_MESSAGE) - .putExtra(MasterService.Constants.EXTENDED_DATA, message); - mContext.sendBroadcast(intent); - } catch (Exception ignored) { - } - } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/android/utils/FormatKit.java b/app/src/main/java/info/nightscout/android/utils/FormatKit.java index a66e9ca9485b5e8bcb454f403eb0771ead164aa4..86488f26b02e7e38f7900742efb46354d040c62b 100644 --- a/app/src/main/java/info/nightscout/android/utils/FormatKit.java +++ b/app/src/main/java/info/nightscout/android/utils/FormatKit.java @@ -15,6 +15,8 @@ import java.util.Locale; import info.nightscout.android.R; +import static info.nightscout.android.medtronic.MainActivity.MMOLXLFACTOR; + /** * Created by Pogman on 19.04.18. */ @@ -25,8 +27,6 @@ public class FormatKit { private static FormatKit sInstance; private final Application mApplication; - private final float MMOLXLFACTOR = 18.016f; - private FormatKit(Application application) { Log.d(TAG, "initialise instance"); mApplication = application; @@ -73,8 +73,14 @@ public class FormatKit { public String formatAsGlucose(int value, boolean units) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApplication.getApplicationContext()); - if (sharedPreferences.getBoolean("mmolxl", false)) return formatAsGlucoseMMOL(value, units); - return formatAsGlucoseMGDL(value, units); + if (!sharedPreferences.getBoolean("mmolxl", false)) return formatAsGlucoseMGDL(value, units); + return formatAsGlucoseMMOL(value, units, false); + } + + public String formatAsGlucose(int value, boolean units, boolean decimals) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApplication.getApplicationContext()); + if (!sharedPreferences.getBoolean("mmolxl", false)) return formatAsGlucoseMGDL(value, units); + return formatAsGlucoseMMOL(value, units, decimals ? sharedPreferences.getBoolean("mmolDecimals", false) : false); } public String formatAsGlucoseMGDL(int value, boolean units) { @@ -82,8 +88,17 @@ public class FormatKit { return df.format(value) + (units ? " " + mApplication.getApplicationContext().getString(R.string.text_unit_mgdl) : ""); } + public String formatAsGlucoseMMOL(int value, boolean units, boolean decimals) { + DecimalFormat df; + if (decimals) + df = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.getDefault())); + else + df = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.getDefault())); + return df.format(value / MMOLXLFACTOR) + (units ? " " + mApplication.getApplicationContext().getString(R.string.text_unit_mmol) : ""); + } + public String formatAsGlucoseMMOL(int value, boolean units) { - DecimalFormat df = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.getDefault())); + DecimalFormat df = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.getDefault())); return df.format(value / MMOLXLFACTOR) + (units ? " " + mApplication.getApplicationContext().getString(R.string.text_unit_mmol) : ""); } @@ -95,6 +110,26 @@ public class FormatKit { DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.getDefault())); return df.format(value); } +/* + public String formatSecondsAsDiff(int seconds) { + int d = Math.abs(seconds) / 86400; + int h = (Math.abs(seconds) % 86400) / 3600; + int m = (Math.abs(seconds) % 3600) / 60; + int s = Math.abs(seconds) % 60; + + String t = seconds < 0 ? "-" : "+"; + if (d > 0) t += d + mApplication.getApplicationContext().getString(R.string.time_d); + else if (h > 0) t += h + mApplication.getApplicationContext().getString(R.string.time_h); + else if (m > 0) t += m + mApplication.getApplicationContext().getString(R.string.time_m); + else t += s + mApplication.getApplicationContext().getString(R.string.time_s); + + return t; + } + */ + + public String formatSecondsAsDiff(int seconds) { + return (seconds < 0 ? "-" : "+") + formatSecondsAsDHMS(Math.abs(seconds)); + } public String formatSecondsAsDHMS(int seconds) { int d = seconds / 86400; @@ -142,18 +177,50 @@ public class FormatKit { return Integer.toString(value) + "%"; } - public String formatAsClock(Date date) { - return formatAsClock(date.getTime()); + public String formatAsClock(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("HH:mm", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("h:mm a", Locale.getDefault()).format(time) + .replace(".", "").replace(",", ""); } - public String formatAsClock(long time) { - if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) { - SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault()); - return sdf.format(time); - } else { - SimpleDateFormat sdf = new SimpleDateFormat("h:mma", Locale.getDefault()); - return sdf.format(time).toLowerCase().replace(".", "").replace(",", ""); - } + public String formatAsClockNoAmPm(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("HH:mm", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("h:mm", Locale.getDefault()).format(time); + } + + public String formatAsClockSeconds(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("h:mm:ss a", Locale.getDefault()).format(time) + .replace(".", "").replace(",", ""); + } + + public String formatAsClockSecondsNoAmPm(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("h:mm:ss", Locale.getDefault()).format(time); + } + + public String formatAsDayClock(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("E HH:mm", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("E h:mm a", Locale.getDefault()).format(time) + .replace(".", "").replace(",", ""); + } + + public String formatAsDayClockSeconds(long time) { + if (DateFormat.is24HourFormat(mApplication.getApplicationContext())) + return new SimpleDateFormat("E HH:mm:ss", Locale.getDefault()).format(time); + else + return new SimpleDateFormat("E h:mm:ss a", Locale.getDefault()).format(time) + .replace(".", "").replace(",", ""); } public String formatAsClock(int hours, int minutes) { @@ -163,7 +230,7 @@ public class FormatKit { } else { return (hours > 12 ? hours - 12 : hours) + ":" + df.format(minutes) + DateFormatSymbols.getInstance().getAmPmStrings()[hours < 12 ? 0 : 1] - .toLowerCase().replace(".", "").replace(",", ""); + .replace(".", "").replace(",", ""); } } @@ -177,11 +244,6 @@ public class FormatKit { return sdf.format(time); } - public String formatAsWeekday(Date date) { - SimpleDateFormat sdf = new SimpleDateFormat("EEEE", Locale.getDefault()); - return sdf.format(date); - } - public String formatAsWeekdayMonthDay(long time) { SimpleDateFormat sdf = new SimpleDateFormat("EEEE MMMM dd", Locale.getDefault()); return sdf.format(time); @@ -193,7 +255,7 @@ public class FormatKit { } public String formatAsYMD(long time) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()); return sdf.format(time); } diff --git a/app/src/main/java/info/nightscout/android/utils/RealmKit.java b/app/src/main/java/info/nightscout/android/utils/RealmKit.java new file mode 100644 index 0000000000000000000000000000000000000000..05c120517575dd7761feeba620c0758747462719 --- /dev/null +++ b/app/src/main/java/info/nightscout/android/utils/RealmKit.java @@ -0,0 +1,71 @@ +package info.nightscout.android.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import java.util.Date; + +import info.nightscout.android.UploaderApplication; +import info.nightscout.android.medtronic.UserLogMessage; +import io.realm.Realm; + +public class RealmKit { + private static final String TAG = RealmKit.class.getSimpleName(); + + public static synchronized void compact(Context context) { + Log.d(TAG, "compactRealm called"); + + try { + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + long now = System.currentTimeMillis(); + long compact = now - 24 * 60 * 60000L; + long lastrun; + + StringBuilder sb = new StringBuilder(); + + lastrun = sharedPreferences.getLong("RealmCompactTimestampDefault", 0); + Log.d(TAG, String.format("compactRealm: last run on default: %s", new Date(lastrun).toString())); + if (lastrun < compact && Realm.compactRealm(Realm.getDefaultConfiguration())) { + Log.i(TAG, "compactRealm: compacting default successful"); + sb.append(" default"); + sharedPreferences.edit().putLong("RealmCompactTimestampDefault", now).apply(); + } + + lastrun = sharedPreferences.getLong("RealmCompactTimestampStore", 0); + Log.d(TAG, String.format("compactRealm: last run on store: %s", new Date(lastrun).toString())); + if (lastrun < compact && Realm.compactRealm(UploaderApplication.getStoreConfiguration())) { + Log.i(TAG, "compactRealm: compacting store successful"); + sb.append(" store"); + sharedPreferences.edit().putLong("RealmCompactTimestampStore", now).apply(); + } + + lastrun = sharedPreferences.getLong("RealmCompactTimestampUserlog", 0); + Log.d(TAG, String.format("compactRealm: last run on userlog: %s", new Date(lastrun).toString())); + if (lastrun < compact && Realm.compactRealm(UploaderApplication.getUserLogConfiguration())) { + Log.i(TAG, "compactRealm: compacting userlog successful"); + sb.append(" userlog"); + sharedPreferences.edit().putLong("RealmCompactTimestampUserlog", now).apply(); + } + + lastrun = sharedPreferences.getLong("RealmCompactTimestampHistory", 0); + Log.d(TAG, String.format("compactRealm: last run on history: %s", new Date(lastrun).toString())); + if (lastrun < compact && Realm.compactRealm(UploaderApplication.getHistoryConfiguration())) { + Log.i(TAG, "compactRealm: compacting history successful"); + sb.append(" history"); + sharedPreferences.edit().putLong("RealmCompactTimestampHistory", now).apply(); + } + + if (sb.length() > 0) { + UserLogMessage.getInstance().addAsync(UserLogMessage.TYPE.NOTE, UserLogMessage.FLAG.EXTENDED, + "Compact Realm:" + sb.toString()); + } + + } catch (Exception e) { + Log.e(TAG, "Error trying to compact realm" + Log.getStackTraceString(e)); + } + } + +} diff --git a/app/src/main/java/info/nightscout/android/utils/ToolKit.java b/app/src/main/java/info/nightscout/android/utils/ToolKit.java index 6839ea85fa4b616bf7be413956df8a59527e153d..fd492cd8487cb6c0dd405ca54f52db542868a6b1 100644 --- a/app/src/main/java/info/nightscout/android/utils/ToolKit.java +++ b/app/src/main/java/info/nightscout/android/utils/ToolKit.java @@ -94,11 +94,11 @@ public class ToolKit { } public static String readString(byte[] data, int offset, int size) { - String string = ""; + StringBuilder sb = new StringBuilder(); for (int i = 0; i < size; i++) { - string += (char) data[offset + i]; + sb.append((char) data[offset + i]); } - return string; + return sb.toString(); } } diff --git a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadService.java b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadService.java index e3a85add64b68b93ee286ce8c3c87cff0cc4eb70..02c814ee099ed05cd188eb5fe551b2610da4c209 100644 --- a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadService.java +++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadService.java @@ -3,12 +3,11 @@ package info.nightscout.android.xdrip_plus; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; -import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.util.Log; import org.json.JSONArray; @@ -19,12 +18,14 @@ import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; -import info.nightscout.android.R; +import info.nightscout.android.UploaderApplication; import info.nightscout.android.history.PumpHistoryHandler; +import info.nightscout.android.medtronic.UserLogMessage; import info.nightscout.android.medtronic.service.MasterService; import info.nightscout.android.model.medtronicNg.PumpHistoryCGM; import info.nightscout.android.model.medtronicNg.PumpHistoryInterface; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; +import info.nightscout.android.model.store.DataStore; import io.realm.Realm; import io.realm.RealmResults; import io.realm.Sort; @@ -42,6 +43,10 @@ public class XDripPlusUploadService extends Service { private Context mContext; + private Realm mRealm; + private Realm storeRealm; + private DataStore dataStore; + private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); private String device; @@ -84,130 +89,144 @@ public class XDripPlusUploadService extends Service { PowerManager.WakeLock wl = getWakeLock(mContext, TAG, 60000); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - Boolean enableXdripPlusUpload = prefs.getBoolean(getString(R.string.preference_enable_xdrip_plus), false); + storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration()); + dataStore = storeRealm.where(DataStore.class).findFirst(); - if (enableXdripPlusUpload) { + if (dataStore.isEnableXdripPlusUpload()) { device = "NA"; - Realm mRealm = Realm.getDefaultInstance(); - - RealmResults<PumpStatusEvent> pumpStatusEvents = mRealm - .where(PumpStatusEvent.class) - .sort("eventDate", Sort.DESCENDING) - .findAll(); - - if (pumpStatusEvents.size() > 0) { - device = pumpStatusEvents.first().getDeviceName(); - doXDripUploadStatus(pumpStatusEvents.first()); - } - + mRealm = Realm.getDefaultInstance(); PumpHistoryHandler pumpHistoryHandler = new PumpHistoryHandler(mContext); - List<PumpHistoryInterface> records = pumpHistoryHandler.getSenderRecordsREQ("XD"); - for (PumpHistoryInterface record : records) { - if (((PumpHistoryCGM) record).getSgv() > 0) doXDripUploadCGM((PumpHistoryCGM) record, device); + try { + + RealmResults<PumpStatusEvent> pumpStatusEvents = mRealm + .where(PumpStatusEvent.class) + .sort("eventDate", Sort.DESCENDING) + .findAll(); + + if (pumpStatusEvents.size() > 0) { + device = pumpStatusEvents.first().getDeviceName(); + doXDripUploadStatus(pumpStatusEvents.first()); + } + + List<PumpHistoryInterface> records = pumpHistoryHandler.getSenderRecordsREQ("XD"); + + for (PumpHistoryInterface record : records) { + if (((PumpHistoryCGM) record).getSgv() > 0) doXDripUploadCGM((PumpHistoryCGM) record, device); + } + + pumpHistoryHandler.setSenderRecordsACK(records, "XD"); + + if (!dataStore.isXdripPlusUploadAvailable()) { + UserLogMessage.send(mContext, UserLogMessage.TYPE.SHARE, + "Xdrip is available"); + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + dataStore.setXdripPlusUploadAvailable(true); + } + }); + } + + } catch (Exception e) { + UserLogMessage.send(mContext, UserLogMessage.TYPE.WARN, + "Xdrip is not available"); + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(@NonNull Realm realm) { + dataStore.setXdripPlusUploadAvailable(false); + } + }); } - pumpHistoryHandler.setSenderRecordsACK(records, "XD"); pumpHistoryHandler.close(); - mRealm.close(); } + storeRealm.close(); + releaseWakeLock(wl); stopSelf(); } } - private void doXDripUploadCGM(PumpHistoryCGM record, String device) { - try { - JSONArray entriesBody = new JSONArray(); - JSONObject json = new JSONObject(); + private void doXDripUploadCGM(PumpHistoryCGM record, String device) throws Exception { + JSONArray entriesBody = new JSONArray(); + JSONObject json = new JSONObject(); - json.put("device", device); - json.put("type", "sgv"); - json.put("date", record.getEventDate().getTime()); - json.put("dateString", record.getEventDate()); - json.put("sgv", record.getSgv()); - String trend = record.getCgmTrend(); - if (trend != null) json.put("direction", PumpHistoryCGM.NS_TREND.valueOf(trend).string()); + json.put("device", device); + json.put("type", "sgv"); + json.put("date", record.getEventDate().getTime()); + json.put("dateString", record.getEventDate()); + json.put("sgv", record.getSgv()); + String trend = record.getCgmTrend(); + if (trend != null) json.put("direction", PumpHistoryCGM.NS_TREND.valueOf(trend).string()); - entriesBody.put(json); - sendBundle(mContext, "add", "entries", entriesBody); - } catch (Exception e) { - Log.e(TAG, "Unable to send bundle: " + e); - } + entriesBody.put(json); + sendBundle(mContext, "add", "entries", entriesBody); } - private void doXDripUploadStatus(PumpStatusEvent record) { - try { - final JSONArray devicestatusBody = new JSONArray(); + private void doXDripUploadStatus(PumpStatusEvent record) throws Exception { + final JSONArray devicestatusBody = new JSONArray(); - JSONObject json = new JSONObject(); - json.put("uploaderBattery", MasterService.getUploaderBatteryLevel()); - json.put("device", record.getDeviceName()); - json.put("created_at", ISO8601_DATE_FORMAT.format(record.getEventDate())); + JSONObject json = new JSONObject(); + json.put("uploaderBattery", MasterService.getUploaderBatteryLevel()); + json.put("device", record.getDeviceName()); + json.put("created_at", ISO8601_DATE_FORMAT.format(record.getEventDate())); - JSONObject pumpInfo = new JSONObject(); - pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getEventDate())); - pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP)); + JSONObject pumpInfo = new JSONObject(); + pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getEventDate())); + pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP)); - JSONObject iob = new JSONObject(); - iob.put("timestamp", record.getEventDate()); - iob.put("bolusiob", record.getActiveInsulin()); + JSONObject iob = new JSONObject(); + iob.put("timestamp", record.getEventDate()); + iob.put("bolusiob", record.getActiveInsulin()); - JSONObject battery = new JSONObject(); - battery.put("percent", record.getBatteryPercentage()); + JSONObject battery = new JSONObject(); + battery.put("percent", record.getBatteryPercentage()); - pumpInfo.put("iob", iob); - pumpInfo.put("battery", battery); - json.put("pump", pumpInfo); + pumpInfo.put("iob", iob); + pumpInfo.put("battery", battery); + json.put("pump", pumpInfo); - devicestatusBody.put(json); - sendBundle(mContext, "add", "devicestatus", devicestatusBody); - } catch (Exception e) { - Log.e(TAG, "Unable to send bundle: " + e); - } + devicestatusBody.put(json); + sendBundle(mContext, "add", "devicestatus", devicestatusBody); } + private void doXDripUpload(List<PumpStatusEvent> records) throws Exception { + final JSONArray devicestatusBody = new JSONArray(); + final JSONArray entriesBody = new JSONArray(); + for (PumpStatusEvent record : records) { + addDeviceStatus(devicestatusBody, record); + addSgvEntry(entriesBody, record); + addMbgEntry(entriesBody, record); + } - private void doXDripUpload(List<PumpStatusEvent> records) { - try { - - final JSONArray devicestatusBody = new JSONArray(); - final JSONArray entriesBody = new JSONArray(); - - for (PumpStatusEvent record : records) { - addDeviceStatus(devicestatusBody, record); - addSgvEntry(entriesBody, record); - addMbgEntry(entriesBody, record); - } - - if (entriesBody.length() > 0) { - sendBundle(mContext, "add", "entries", entriesBody); - } - if (devicestatusBody.length() > 0) { - sendBundle(mContext, "add", "devicestatus", devicestatusBody); - } - } catch (Exception e) { - Log.e(TAG, "Unable to send bundle: " + e); + if (entriesBody.length() > 0) { + sendBundle(mContext, "add", "entries", entriesBody); + } + if (devicestatusBody.length() > 0) { + sendBundle(mContext, "add", "devicestatus", devicestatusBody); } } - private void sendBundle(Context context, String action, String collection, JSONArray json) { + private void sendBundle(Context context, String action, String collection, JSONArray json) throws Exception { final Bundle bundle = new Bundle(); bundle.putString("action", action); bundle.putString("collection", collection); bundle.putString("data", json.toString()); + final Intent intent = new Intent(Constants.XDRIP_PLUS_NS_EMULATOR); intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); context.sendBroadcast(intent); + List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(intent, 0); if (receivers.size() < 1) { - Log.w(TAG, "No xDrip receivers found. "); + Log.w(TAG, "No xDrip receivers found."); + throw new Exception("No xDrip receivers found."); } else { Log.d(TAG, receivers.size() + " xDrip receivers"); } diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..7dc91c19028d861598c75465fe3f5a6cc61985b7 --- /dev/null +++ b/app/src/main/res/layout-land/activity_main.xml @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.design.widget.AppBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_scrollFlags="scroll|enterAlways" + app:popupTheme="@style/ThemeOverlay.AppCompat.Light" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> + </android.support.v7.widget.Toolbar> + + </android.support.design.widget.AppBarLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="30dp" + android:layout_marginTop="10sp" + android:layout_marginRight="30dp" + android:layout_marginBottom="10sp" + android:baselineAligned="false" + android:gravity="center"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:baselineAligned="false" + android:gravity="center" + android:paddingLeft="30dp"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:baselineAligned="false"> + + <TextView + android:id="@+id/textview_bg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_weight="1" + android:maxLines="1" + android:text="@string/text_dash" + android:textAlignment="center" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="80dp" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_weight="1" + android:gravity="bottom|center_horizontal" + android:orientation="vertical"> + + <com.mikepenz.iconics.view.IconicsTextView + android:id="@+id/textview_trend" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center|top" + android:maxLines="1" + android:text="@string/text_dash" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="60sp" /> + + <TextView + android:id="@+id/textview_units" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:maxLines="1" + android:text="@string/text_unit_mgdl" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + </LinearLayout> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="10sp"> + + <TextView + android:id="@+id/textview_bg_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:layout_marginTop="5sp" + android:text="@string/main_never" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:orientation="horizontal" + android:paddingTop="10sp"> + + <TextView + android:id="@+id/label_iob" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/main_active_insulin" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <TextView + android:id="@+id/textview_iob" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="5sp" + android:layout_marginLeft="5sp" + android:text="@string/text_dash" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <TextView + android:id="@+id/label_units" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="5sp" + android:layout_marginLeft="5sp" + android:text="@string/text_insulin_unit" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + </LinearLayout> + </LinearLayout> + </LinearLayout> + + </LinearLayout> + + <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.jjoe64.graphview.GraphView + android:id="@+id/chart" + android:gravity="center_horizontal" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintDimensionRatio="333:100" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + + </android.support.constraint.ConstraintLayout> + + <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible"> + + <co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView + android:id="@+id/recyclerview_log" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:rrvLayoutType="LinearLayout" + app:rrvSwipeToDelete="false" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab_log_current" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_margin="16dp" + android:src="@android:drawable/ic_menu_revert" + app:backgroundTint="#40000000" + app:fabSize="mini" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab_log_search" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_margin="16dp" + android:src="@android:drawable/ic_menu_search" + app:backgroundTint="#40000000" + app:fabSize="mini" /> + + </android.support.design.widget.CoordinatorLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d3fb520b5f97b361fb745782b8a3e717b04d4963..337827a0ff050b51ab586afa4c5a1906977b9365 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> @@ -124,81 +125,56 @@ </LinearLayout> - <android.support.percent.PercentRelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" + <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> + <com.jjoe64.graphview.GraphView - app:layout_widthPercent="100%" - app:layout_aspectRatio="333%" - app:layout_marginTopPercent="0%" - app:layout_marginLeftPercent="0%" - android:id="@+id/chart"/> - </android.support.percent.PercentRelativeLayout> - - <ScrollView - android:id="@+id/scrollView" + android:id="@+id/chart" + android:gravity="center_horizontal" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintDimensionRatio="333:100" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + </android.support.constraint.ConstraintLayout> + + <android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="fill_parent"> + android:layout_height="match_parent"> - <LinearLayout - android:orientation="vertical" + <co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView + android:id="@+id/recyclerview_log" android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="-10sp" - android:layout_gravity="center_horizontal" - android:orientation="horizontal"> - - <Button - android:id="@+id/button_log_top" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/older_log_entries" /> - - <Button - android:id="@+id/button_log_top_recent" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/most_recent" /> - </LinearLayout> - - <com.mikepenz.iconics.view.IconicsTextView - android:id="@+id/textview_log" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_margin="10sp" - android:gravity="bottom" - android:text="" - android:layout_weight="0.33" /> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="-10sp" - android:layout_gravity="center_horizontal" - android:orientation="horizontal"> - - <Button - android:id="@+id/button_log_bottom" - android:layout_gravity="center_horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/newer_log_entries" /> + android:layout_height="wrap_content" + app:rrvLayoutType="LinearLayout" + app:rrvSwipeToDelete="false" /> - <Button - android:id="@+id/button_log_bottom_recent" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/most_recent" /> - </LinearLayout> + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab_log_current" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_margin="16dp" + android:src="@android:drawable/ic_menu_revert" + app:backgroundTint="#40000000" + app:fabSize="mini" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab_log_search" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_margin="16dp" + android:src="@android:drawable/ic_menu_search" + app:backgroundTint="#40000000" + app:fabSize="mini" /> - </LinearLayout> - </ScrollView> + </android.support.design.widget.CoordinatorLayout> </LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/log_item.xml b/app/src/main/res/layout/log_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d16ee133e952ab2469dd6c7bd3bd860a011f832 --- /dev/null +++ b/app/src/main/res/layout/log_item.xml @@ -0,0 +1,13 @@ +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/log_textview" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10sp" + android:layout_marginRight="10sp"/> + +</FrameLayout> diff --git a/app/src/main/res/values-v14/dimens.xml b/app/src/main/res/values-v14/dimens.xml deleted file mode 100644 index 55c1e5908c7e0f157fe815acd6d5cd7358463390..0000000000000000000000000000000000000000 --- a/app/src/main/res/values-v14/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ -<resources> - - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> - -</resources> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 55c1e5908c7e0f157fe815acd6d5cd7358463390..01681b880fb7299f009b81c3f56425a68d057210 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,5 +3,6 @@ <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> + <dimen name="text_margin">16dp</dimen> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 69156725f7f0313fd0d9dcf05f0f64bebd997c0d..5017a14038c67000d215443f5535b1df9526da09 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,15 +93,16 @@ <string name="newer_log_entries">newer log entries</string> <string name="connecting_to_contour_next_link">Connecting to Contour Next Link</string> - <string name="connected_on_channel_rssi">Connected on channel %d RSSI: %d%%</string> + <string name="connected_on_channel">Connected on channel</string> + <string name="rssi">RSSI</string> <string name="please_wait">Please wait</string> - <string name="pump_is_expecting_sensor_communication">Pump is expecting sensor communication. Poll due in %d seconds."</string> + <string name="pump_is_expecting_sensor_communication">Pump is expecting sensor communication. Poll due in</string> <string name="radio_clash_protection_is_disabled">Radio clash protection is disabled.</string> <string name="could_not_communicate_with_the_pump">Could not communicate with the pump. Is it nearby?</string> - <string name="polling_service_could_not_complete">Polling service could not complete. Will try again in %d seconds.</string> + <string name="polling_service_could_not_complete">Polling service could not complete. Will try again in 60 seconds.</string> <string name="unexpected_error">Unexpected Error!</string> - <string name="next_poll_due_at">Next poll due at: %s</string> + <string name="next_poll_due_at">Next poll due at</string> <string name="history_text">history</string> <string name="history_read_pump">PUMP history</string> @@ -149,7 +150,9 @@ <string name="warn_pump_battery_low">Warning: pump battery low</string> <string name="info_pump_low_battery_mode_change">Poll interval increased, history and backfill processing reduced.</string> <string name="info_low_battery_interval">Low battery poll interval:</string> - <string name="warn_pump_time_difference">Warning: Time difference between Pump and Uploader excessive. Pump is over %d %s %s of time used by uploader.</string> + <string name="warn_pump_time_difference">Warning: Time difference between Pump and Uploader excessive. Pump is over</string> + <string name="warn_pump_time_difference_ahead">ahead of the time used by uploader.</string> + <string name="warn_pump_time_difference_behind">behind the time used by uploader.</string> <string name="help_pump_time_difference">The uploader phone/device should have the current time provided by network. Pump clock drifts forward and needs to be set to correct time occasionally.</string> <string name="warn_multiple_comms_errors">Warning: multiple comms/timeout errors detected.</string> <string name="help_multiple_comms_errors">Try: disconnecting and reconnecting the Contour Next Link to phone / restarting phone / check pairing of CNL with Pump.</string> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index a06e368a9f4c866136a597fbb1414f1376121d05..6abddb9d204205e4f865eb2d4562994aaf497c00 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1438,6 +1438,7 @@ android:title="@string/pref_disclaimer_title"/> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_category_app_version"> + <!--suppress AndroidDomInspection --> <Preference android:key="version" android:title="@string/versionName" /> diff --git a/build.gradle b/build.gradle index d5977bdfe0dddd8b041f3e3ebf081b0bdc3ee779..da5c8aa090f80b2a1a593fc4c3aebe5307b6b627 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' + classpath 'com.android.tools.build:gradle:3.2.1' } }